From cbdace7e8760f5ff5407f484c968cfd5d4656cee Mon Sep 17 00:00:00 2001 From: BBcan177 Date: Sun, 22 Feb 2015 19:51:51 -0500 Subject: pfBlockerNG - Mods for Nano Aliastables ro/rw Add RW/RO commands for Aliastables Archiving for Nano/Ramdisk Installs. --- config/pfblockerng/pfblockerng.inc | 14 +- config/pfblockerng/pfblockerng.inc.bak | 2701 ++++++++++++++++++++++++++++++++ 2 files changed, 2712 insertions(+), 3 deletions(-) create mode 100644 config/pfblockerng/pfblockerng.inc.bak diff --git a/config/pfblockerng/pfblockerng.inc b/config/pfblockerng/pfblockerng.inc index 78622631..6ee9592a 100644 --- a/config/pfblockerng/pfblockerng.inc +++ b/config/pfblockerng/pfblockerng.inc @@ -343,10 +343,11 @@ function pfb_aliastables($mode) { // Only Execute function if Platform is NanoBSD or Ramdisks are used. if (($g['platform'] != "pfSense") || isset($config['system']['use_mfs_tmpvar'])) { + conf_mount_rw(); if ($mode == "update") { // Archive Aliastable Folder exec ("cd {$pfb['aliasdir']}; ls -A pfB_*.txt && /usr/bin/tar -jcvf {$pfb['aliasarchive']} pfB_*.txt >/dev/null 2>&1"); - $msg = "\n\nArchiving Aliastable Folder"; + $msg = "\n\nArchiving Aliastable Folder\n"; } elseif ($mode == "conf") { // Check conf file for earlyshellcmd @@ -362,10 +363,16 @@ function pfb_aliastables($mode) { $msg = "\n** Adding earlyshellcmd **\n"; } } + conf_mount_ro(); } else { - // Remove Aliastables archive and earlyshellcmd if found. - @unlink_if_exists("{$pfb['aliasarchive']}"); + if (file_exists("{$pfb['aliasarchive']}")) { + // Remove Aliastables archive if found. + conf_mount_rw(); + @unlink_if_exists("{$pfb['aliasarchive']}"); + conf_mount_ro(); + } + // Remove earlyshellcmd if found. if (is_array($config['system']['earlyshellcmd'])) { $a_earlyshellcmd = &$config['system']['earlyshellcmd']; if (preg_grep("/pfblockerng.sh aliastables/", $a_earlyshellcmd)) { @@ -2231,6 +2238,7 @@ function sync_package_pfblockerng($cron = "") { unset ($other_rules,$fother_rules,$permit_rules,$fpermit_rules,$match_rules,$fmatch_rules); } + ################################# # Closing Processes # ################################# diff --git a/config/pfblockerng/pfblockerng.inc.bak b/config/pfblockerng/pfblockerng.inc.bak new file mode 100644 index 00000000..da5a5261 --- /dev/null +++ b/config/pfblockerng/pfblockerng.inc.bak @@ -0,0 +1,2701 @@ + /tmp/pfblog; /bin/mv -f /tmp/pfblog {$pfb['log']}"); + } +} + + +# Record Log Messsages to pfBlockerNG Log File and/or Error Log File. +function pfb_logger($log, $type) { + global $g,$pfb,$pfbarr; + + $now = date("m/d/y G:i:s", time()); + + # Only log timestamp if new + if (preg_match("/NOW/", $log)) { + if ($now == $pfb['pnow']) { + $log = str_replace("[ NOW ]", "", "{$log}"); + } else { + $log = str_replace("NOW", $now, "{$log}"); + } + $pfb['pnow'] = "{$now}"; + } + + if ($type == 2) { + @file_put_contents("{$pfb['log']}", "{$log}", FILE_APPEND); + @file_put_contents("{$pfb['errlog']}", "{$log}", FILE_APPEND); + } elseif ($type == 3) { + @file_put_contents("{$pfb['geolog']}", "{$log}", FILE_APPEND); + } else { + @file_put_contents("{$pfb['log']}", "{$log}", FILE_APPEND); + } +} + + +# Determine Folder Location for 'List' +function pfb_determine_list_detail($list) { + global $g,$pfb,$pfbarr; + $pfbarr = array(); + + if (in_array($list,array('Match_Both','Match_Inbound','Match_Outbound','Alias_Match'))) { + $pfbarr['skip'] = FALSE; + $pfbarr['folder'] = "{$pfb['matchdir']}"; + } elseif (in_array($list,array('Permit_Both','Permit_Inbound','Permit_Outbound','Alias_Permit'))) { + $pfbarr['skip'] = FALSE; + $pfbarr['folder'] = "{$pfb['permitdir']}"; + } elseif ($list == "Alias_Native") { + $pfbarr['skip'] = FALSE; + $pfbarr['folder'] = "{$pfb['nativedir']}"; + } else { + # Deny + $pfbarr['skip'] = TRUE; + $pfbarr['folder'] = "{$pfb['denydir']}"; + } + + // Collect proper Alias Table Description (Alias Only vs AutoRules) + if (preg_match("/Alias/", $list)) { + $pfbarr['descr'] = ""; + } else { + $pfbarr['descr'] = " Auto "; + } + + return $pfbarr; +} + +# Create Suppression Alias +function pfb_create_suppression_alias() { + global $config; + + // Collect existing pfsense alias(s) + if (is_array($config['aliases']['alias'])) { + foreach($config['aliases']['alias'] as $exalias) { + $new_aliases[] = $exalias; + } + } + // Create New pfBlockerNGSuppress Alias + $new_aliases[] = array( "name" => "pfBlockerNGSuppress", + "address" => "", + "descr" => "pfBlockerNG Suppression List (24|32 CIDR only)", + "type" => "network", + "detail" => "" + ); + $config['aliases']['alias'] = $new_aliases; + write_config(); +} + + +# Create Suppression file from Alias +function pfb_create_suppression_file() { + global $config,$pfb; + + // Find pfBlockerNGSuppress Array ID Number + $pfb['found'] = FALSE; + if (is_array($config['aliases']['alias'])) { + $pfb_id = 0; + foreach ($config['aliases']['alias'] as $alias) { + if ($alias['name'] == "pfBlockerNGSuppress") { + $pfb['found'] = TRUE; + break; + } + $pfb_id++; + } + + if ($pfb['found']) { + $pfb_suppress = str_replace(" ", "\n", $config['aliases']['alias'][$pfb_id]['address']); + if (!empty($pfb_suppress)) + @file_put_contents("{$pfb['supptxt']}",$pfb_suppress, LOCK_EX); + } else { + # Delete Suppression File if Alias is Empty. + unlink_if_exists("{$pfb['supptxt']}"); + } + } + + // Call Function to Create Suppression Alias. + if (!$pfb['found']) + pfb_create_suppression_alias(); +} + + +// IPv6 Range to CIDR function used courtesey from: +// https://github.com/stilez/pfsense-leases/blob/50cc0fa81dba5fe91bcddaea016c245d1b8479cc/etc/inc/util.inc +function ip_range_to_subnet_array_temp2($ip1, $ip2) { + + if (is_ipaddrv4($ip1) && is_ipaddrv4($ip2)) { + $proto = 'ipv4'; // for clarity + $bits = 32; + $ip1bin = decbin(ip2long32($ip1)); + $ip2bin = decbin(ip2long32($ip2)); + } elseif (is_ipaddrv6($ip1) && is_ipaddrv6($ip2)) { + $proto = 'ipv6'; + $bits = 128; + $ip1bin = Net_IPv6::_ip2Bin($ip1); + $ip2bin = Net_IPv6::_ip2Bin($ip2); + } else + return array(); + + // it's *crucial* that binary strings are guaranteed the expected length; do this for certainty even though for IPv6 it's redundant + $ip1bin = str_pad($ip1bin, $bits, '0', STR_PAD_LEFT); + $ip2bin = str_pad($ip2bin, $bits, '0', STR_PAD_LEFT); + + if ($ip1bin === $ip2bin) + return array($ip1 . '/' . $bits); + + if (strcmp($ip1bin, $ip2bin) > 0) + list ($ip1bin, $ip2bin) = array($ip2bin, $ip1bin); // swap contents of ip1 <= ip2 + + $rangesubnets = array(); + $netsize = 0; + + do { + // at loop start, $ip1 is guaranteed strictly less than $ip2 (important for edge case trapping and preventing accidental binary wrapround) + // which means the assignments $ip1 += 1 and $ip2 -= 1 will always be "binary-wrapround-safe" + + // step #1 if start ip (as shifted) ends in any '1's, then it must have a single cidr to itself (any cidr would include the '0' below it) + + if (substr($ip1bin, -1, 1) == '1') { + // the start ip must be in a separate one-IP cidr range + $new_subnet_ip = substr($ip1bin, $netsize, $bits - $netsize) . str_repeat('0', $netsize); + $rangesubnets[$new_subnet_ip] = $bits - $netsize; + $n = strrpos($ip1bin, '0'); //can't be all 1's + $ip1bin = ($n == 0 ? '' : substr($ip1bin, 0, $n)) . '1' . str_repeat('0', $bits - $n - 1); // BINARY VERSION OF $ip1 += 1 + } + + // step #2, if end ip (as shifted) ends in any zeros then that must have a cidr to itself (as cidr cant span the 1->0 gap) + + if (substr($ip2bin, -1, 1) == '0') { + // the end ip must be in a separate one-IP cidr range + $new_subnet_ip = substr($ip2bin, $netsize, $bits - $netsize) . str_repeat('0', $netsize); + $rangesubnets[$new_subnet_ip] = $bits - $netsize; + $n = strrpos($ip2bin, '1'); //can't be all 0's + $ip2bin = ($n == 0 ? '' : substr($ip2bin, 0, $n)) . '0' . str_repeat('1', $bits - $n - 1); // BINARY VERSION OF $ip2 -= 1 + // already checked for the edge case where end = start+1 and start ends in 0x1, above, so it's safe + } + + // this is the only edge case arising from increment/decrement. + // it happens if the range at start of loop is exactly 2 adjacent ips, that spanned the 1->0 gap. (we will have enumerated both by now) + + if (strcmp($ip2bin, $ip1bin) < 0) + continue; + + // step #3 the start and end ip MUST now end in '0's and '1's respectively + // so we have a non-trivial range AND the last N bits are no longer important for CIDR purposes. + + $shift = $bits - max(strrpos($ip1bin, '0'), strrpos($ip2bin, '1')); // num of low bits which are '0' in ip1 and '1' in ip2 + $ip1bin = str_repeat('0', $shift) . substr($ip1bin, 0, $bits - $shift); + $ip2bin = str_repeat('0', $shift) . substr($ip2bin, 0, $bits - $shift); + $netsize += $shift; + if ($ip1bin === $ip2bin) { + // we're done. + $new_subnet_ip = substr($ip1bin, $netsize, $bits - $netsize) . str_repeat('0', $netsize); + $rangesubnets[$new_subnet_ip] = $bits - $netsize; + continue; + } + + // at this point there's still a remaining range, and either startip ends with '1', or endip ends with '0'. So repeat cycle. + } while (strcmp($ip1bin, $ip2bin) < 0); + + // subnets are ordered by bit size. Re sort by IP ("naturally") and convert back to IPv4/IPv6 + + ksort($rangesubnets, SORT_STRING); + $out = array(); + + foreach ($rangesubnets as $ip => $netmask) { + if ($proto == 'ipv4') { + $i = str_split($ip, 8); + $out[] = implode('.', array( bindec($i[0]),bindec($i[1]),bindec($i[2]),bindec($i[3]))) . '/' . $netmask; + } else + $out[] = Net_IPv6::compress(Net_IPv6::_bin2Ip($ip)) . '/' . $netmask; + } + + return $out; +} + + +// Archive Aliastables for NanoBSD and RAMDisk Installations +function pfb_aliastables($mode) { + global $g,$config,$pfb; + $earlyshellcmd = "/usr/local/pkg/pfblockerng/pfblockerng.sh aliastables"; + $msg = ""; + + // Only Execute function if Platform is NanoBSD or Ramdisks are used. + if (($g['platform'] != "pfSense") || isset($config['system']['use_mfs_tmpvar'])) { + conf_mount_rw(); + if ($mode == "update") { + // Archive Aliastable Folder + exec ("cd {$pfb['aliasdir']}; ls -A pfB_*.txt && /usr/bin/tar -jcvf {$pfb['aliasarchive']} pfB_*.txt >/dev/null 2>&1"); + $msg = "\n\nArchiving Aliastable Folder\n"; + } + elseif ($mode == "conf") { + // Check conf file for earlyshellcmd + if (is_array($config['system']['earlyshellcmd'])) { + $a_earlyshellcmd = &$config['system']['earlyshellcmd']; + if (!preg_grep("/pfblockerng.sh aliastables/", $a_earlyshellcmd)) { + $a_earlyshellcmd[] = "{$earlyshellcmd}"; + $msg = "\n** Adding earlyshellcmd **\n"; + } + } + else { + $config['system']['earlyshellcmd'] = "{$earlyshellcmd}"; + $msg = "\n** Adding earlyshellcmd **\n"; + } + } + conf_mount_ro(); + } + else { + if (file_exists("{$pfb['aliasarchive']}")) { + // Remove Aliastables archive if found. + conf_mount_rw(); + @unlink_if_exists("{$pfb['aliasarchive']}"); + conf_mount_ro(); + } + // Remove earlyshellcmd if found. + if (is_array($config['system']['earlyshellcmd'])) { + $a_earlyshellcmd = &$config['system']['earlyshellcmd']; + if (preg_grep("/pfblockerng.sh aliastables/", $a_earlyshellcmd)) { + $a_earlyshellcmd = preg_grep("/pfblockerng.sh aliastables/", $a_earlyshellcmd, PREG_GREP_INVERT); + $msg = "\n** Removing earlyshellcmd **\n"; + } + } + } + + if ($msg != "") + pfb_logger("{$msg}","1"); +} + + +# Main pfBlockerNG Function +function sync_package_pfblockerng($cron = "") { + + global $g,$config,$pfb,$pfbarr; + pfb_global(); + + # Detect Boot Process or Update via CRON + if (isset($_POST) && $cron == "") { + if (!preg_match("/\w+/",$_POST['__csrf_magic'])) { + log_error("[pfBlockerNG] Sync terminated during boot process."); + return; + } + } + log_error("[pfBlockerNG] Starting sync process."); + + // Force Update - Set 'Save' variable when 'No Updates' found. + if ($cron == "noupdates") { + $pfb['save'] = TRUE; + } + + # Start of pfBlockerNG Logging to 'pfblockerng.log' + if ($pfb['enable'] == "on" && !$pfb['save']) { + $log = " UPDATE PROCESS START [ NOW ]\n"; + pfb_logger("{$log}","1"); + } else { + if ($cron != "noupdates") { + $log = "\n**Saving Configuration [ NOW ] ...\n"; + pfb_logger("{$log}","1"); + } + } + + // Call function for NanoBSD/Ramdisk processes. + pfb_aliastables("conf"); + + # Collect pfSense Max Table Size Entry + $pfb['table_limit'] = ($config['system']['maximumtableentries'] != "" ? $config['system']['maximumtableentries'] : "2000000"); + + # If Table limit not defined, set Default to 2M + $config['system']['maximumtableentries'] = "{$pfb['table_limit']}"; + + # Collect local web gui configuration + $pfb['weblocal'] = ($config['system']['webgui']['protocol'] != "" ? $config['system']['webgui']['protocol'] : "http"); + $pfb['port'] = $config['system']['webgui']['port']; + if ($pfb['port'] == "") { + if ($config['system']['webgui']['protocol'] == "http") { + $pfb['port'] = "80"; + } else { + $pfb['port'] = "443"; + } + } + $pfb['weblocal'] .= "://127.0.0.1:{$pfb['port']}/pfblockerng/pfblockerng.php"; + + # Define Inbound/Outbound Action is not user selected. + $pfb['deny_action_inbound'] = ($pfb['config']['inbound_deny_action'] != "" ? $pfb['config']['inbound_deny_action'] : "block"); + $pfb['deny_action_outbound'] = ($pfb['config']['outbound_deny_action'] != "" ? $pfb['config']['outbound_deny_action'] : "reject"); + + # Validation check to see if the Original pfBlocker package is Enabled + $pfb['validate']= $pfb['config']['pfblocker_cb']; + # User Defined CRON Start Minute + $pfb['min'] = $pfb['config']['pfb_min']; + # Reloads Existing Blocklists without Downloading New Lists + $pfb['reuse'] = $pfb['config']['pfb_reuse']; + # Enable OpenVPN AutoRules + $pfb['openvpn'] = $pfb['config']['openvpn_action']; + # Enable/Disable Floating Auto-Rules + $pfb['float'] = $pfb['config']['enable_float']; + # Enable Remove of Duplicate IPs utilizing Grepcidr + $pfb['dup'] = $pfb['config']['enable_dup']; + # Order of the Auto-Rules + $pfb['order'] = $pfb['config']['pass_order']; + # Suffix used for Auto-Rules + $pfb['suffix'] = $pfb['config']['autorule_suffix']; + + # Reputation Variables + $pfb['config_rep'] = $config['installedpackages']['pfblockerngreputation']['config'][0]; + + # Enable/Disable Reputation + $pfb['rep'] = $pfb['config_rep']['enable_rep']; + # Enable/Disable 'pDup' + $pfb['pdup'] = $pfb['config_rep']['enable_pdup']; + # Enable/Disable 'dDup' + $pfb['dedup'] = ($pfb['config_rep']['enable_dedup'] != "" ? $pfb['config_rep']['enable_dedup'] : "x"); + # 'Max' variable setting for Reputation + $pfb['max'] = ($pfb['config_rep']['p24_max_var'] != "" ? $pfb['config_rep']['p24_max_var'] : "x"); + # 'dMax' variable setting for Reputation + $pfb['dmax'] = ($pfb['config_rep']['p24_dmax_var'] != "" ? $pfb['config_rep']['p24_dmax_var'] : "x"); + # 'pMax' variable setting for Reputation + $pfb['pmax'] = ($pfb['config_rep']['p24_pmax_var'] != "" ? $pfb['config_rep']['p24_pmax_var'] : "x"); + # Action for Whitelist Country Category + $pfb['ccwhite'] = $pfb['config_rep']['ccwhite']; + # Action for Blacklist Country Category + $pfb['ccblack'] = $pfb['config_rep']['ccblack']; + # List of Countries in the Whitelist Category + $pfb['ccexclude']= ($pfb['config_rep']['ccexclude'] != "" ? $pfb['config_rep']['ccexclude'] : "x"); + # Emerging Threats IQRisk Block Categories + $pfb['etblock'] = ($pfb['config_rep']['etblock'] != "" ? $pfb['config_rep']['etblock'] : "x"); + # Emerging Threats IQRisk Match Categories + $pfb['etmatch'] = ($pfb['config_rep']['etmatch'] != "" ? $pfb['config_rep']['etmatch'] : "x"); + # Perform a Force Update on ET Categories + $pfb['etupdate']= $pfb['config_rep']['et_update']; + + # Variables + + # Starting Variable to Skip rep, pdup and dedeup functions if no changes are required + $pfb['dupcheck'] = FALSE; + ## $pfb['save'] is used to determine if User pressed "Save" Button to avoid Collision with CRON. + ## This is defined in each pfBlockerNG XML Files + + # Validation Check to ensure pfBlocker and pfBlockerNG are not running at the same time. + if ($pfb['validate'] == "") { + # Collect pfBlocker Enabled Status from config file + $pfb['validate_chk'] = $config['installedpackages']['pfblocker']['config'][0]['enable_cb']; + if ($pfb['validate_chk'] == "on") { + $log = "\n The Package 'pfBlocker' is currently Enabled. Either Disable pfBlocker, or 'Disable Validation Check' in pfBlockerNG \n"; + pfb_logger("{$log}","1"); + return; + } + } + + + ################################# + # Configure ARRAYS # + ################################# + + $continents = array ( "Africa" => "pfB_Africa", + "Antartica" => "pfB_Antartica", + "Asia" => "pfB_Asia", + "Europe" => "pfB_Europe", + "North America" => "pfB_NAmerica", + "Oceania" => "pfB_Oceania", + "South America" => "pfB_SAmerica", + "Top Spammers" => "pfB_Top", + "Proxy and Satellite" => "pfB_PS" + ); + + #create rules vars and arrays + # Array used to Collect Changes to Aliases to be saved to Config + $new_aliases = array(); + $new_aliases_list = array(); + $continent_existing = array(); + $continent_new = array(); + $permit_inbound = array(); + $permit_outbound = array(); + $deny_inbound = array(); + $deny_outbound = array(); + # An Array of all Aliases (Active and non-Active) + $aliases_list = array(); + # This is an Array of Aliases that Have Updated Lists via CRON/Force Update when 'Reputation' disabled. + $pfb_alias_lists = array(); + # This is an Array of All Active Aliases used when 'Reputation' enabled + $pfb_alias_lists_all = array(); + + # Base Rule Array + $base_rule_reg = array( "id" => "", + "tag" => "", + "tagged" => "", + "max" => "", + "max-src-nodes" => "", + "max-src-conn" => "", + "max-src-states"=> "", + "statetimeout" => "", + "statetype" => "keep state", + "os" => "" + ); + + # Floating Rules, Base Rule Array + $base_rule_float = array("id" => "", + "tag" => "", + "tagged" => "", + "quick" => "yes", + "floating" => "yes", + "max" => "", + "max-src-nodes" => "", + "max-src-conn" => "", + "max-src-states"=> "", + "statetimeout" => "", + "statetype" => "keep state", + "os" => "" + ); + + + ######################################### + # Configure Rule Suffix # + ######################################### + + # Discover if any Rules are AutoRules (If no AutoRules found, $pfb['autorules'] is FALSE, Skip Rules Re-Order ) + # To configure Auto Rule Suffix. pfBlockerNG must be disabled to change Suffix and to avoid Duplicate Rules + $pfb['autorules'] = FALSE; + $pfb['found'] = FALSE; + foreach ($continents as $continent => $pfb_alias) { + if (is_array($config['installedpackages']['pfblockerng' . strtolower(preg_replace('/ /','',$continent))]['config'])) { + $continent_config = $config['installedpackages']['pfblockerng' . strtolower(preg_replace('/ /','',$continent))]['config'][0]; + if ($continent_config['action'] != "Disabled" && in_array($continent_config['action'],array('Deny_Both','Deny_Inbound','Deny_Outbound','Match_Both','Match_Inbound','Match_Outbound','Permit_Both','Permit_Inbound','Permit_Outbound'))) { + $pfb['autorules'] = TRUE; + $pfb['found'] = TRUE; + break; + } + } + } + + $list_type = array ("pfblockernglistsv4", "pfblockernglistsv6"); + foreach ($list_type as $ip_type) { + if ($config['installedpackages'][$ip_type]['config'] != "" && !$pfb['found']) { + foreach($config['installedpackages'][$ip_type]['config'] as $list) { + if ($list['action'] != "Disabled" && in_array($list['action'],array('Deny_Both','Deny_Inbound','Deny_Outbound','Match_Both','Match_Inbound','Match_Outbound','Permit_Both','Permit_Inbound','Permit_Outbound'))) { + $pfb['autorules'] = TRUE; + break; + } + } + } + } + + #Configure Auto Rule Suffix. pfBlockerNG must be disabled to change Suffix and to avoid Duplicate Rules + # Count Number of Rules with 'pfB_' + $count = 0; + if (is_array($config['filter']['rule'])) { + foreach ($config['filter']['rule'] as $rule) { + # Collect any pre-existing Suffix + if (preg_match("/pfB_\w+(\s.*)/",$rule['descr'], $pfb_suffix_real) && $count == 0) { + $pfb_suffix_match = $pfb_suffix_real[1]; + } + # Query for Existing pfB Rules + if (preg_match("/pfB_/",$rule['descr'])) { + $count++; + break; + } + } + } + + # Change Suffix only if No pfB Rules Found and Auto Rules are Enabled. + if ($pfb['autorules'] && $count == 0) { + switch ($pfb['suffix']) { + case "autorule": + $pfb['suffix'] = " auto rule"; + break; + case "standard": + $pfb['suffix'] = ""; + break; + case "ar": + $pfb['suffix'] = " AR"; + break; + } + } else { + if ($pfb['autorules']) { + # Use existing Suffix Match + $pfb['suffix'] = $pfb_suffix_match; + } else { + # Leave Rule Suffix 'Blank' + $pfb['suffix'] = ""; + } + } + + + ######################################################### + # Configure INBOUND/OUTBOUND INTERFACES # + ######################################################### + + # Collect pfSense Interface Order + $ifaces = get_configured_interface_list(); + + if (!empty($pfb['config']['inbound_interface'])) { + # Sort Interface Array to match pfSense Interface order to allow Floating Rules to populate. + $selected_interfaces = explode(",",$pfb['config']['inbound_interface']); + # Sort pfBlockerNG Interface order to pfSense Interface Order + $sort_interfaces = array_intersect($ifaces, $selected_interfaces); + $implode_interfaces = ltrim(implode(",",$sort_interfaces), ","); + # CSV String for Inbound Interfaces for 'pfB_' Match Rules + $pfb['inbound_floating'] = $implode_interfaces; + $pfb['inbound_interfaces_float'] = explode(" ",$implode_interfaces); + + # Assign Inbound Base Rule/Interfaces + if ($pfb['float'] == "on") { + # Define Base Firewall Floating Rules Settings + $base_rule = $base_rule_float; + $pfb['inbound_interfaces'] = $pfb['inbound_interfaces_float']; + } else { + # Define Base Firewall Rules Settings + $base_rule = $base_rule_reg; + $pfb['inbound_interfaces'] = explode(",",$pfb['config']['inbound_interface']); + } + } else { + # Define Empty Variable/Array + $pfb['inbound_interfaces_float'] = ""; + $pfb['inbound_interfaces'] = array(); + } + + if (!empty($pfb['config']['outbound_interface'])) { + # Sort Interface Array to match pfSense Interface order to allow Floating Rules to populate. + $selected_interfaces = explode(",",$pfb['config']['outbound_interface']); + # Sort pfBlockerNG Interface order to pfSense Interface Order + $sort_interfaces = array_intersect($ifaces, $selected_interfaces); + // If OpenVPN Interfaces are not in dropdown menu + if ($pfb['openvpn'] == "on" && $config['openvpn']['openvpn-server'] || $pfb['openvpn'] == "on" && $config['openvpn']['openvpn-client']) + if (!in_array("openvpn",$sort_interfaces)) + array_push($sort_interfaces, "openvpn"); + $implode_interfaces = ltrim(implode(",",$sort_interfaces), ","); + # CSV String for Outbound Interfaces for 'pfB_' Match Rules + $pfb['outbound_floating'] = $implode_interfaces; + $pfb['outbound_interfaces_float'] = explode(" ",$implode_interfaces); + + # Assign Outbound Base Rule/Interfaces + if ($pfb['float'] == "on") { + $base_rule = $base_rule_float; + $pfb['outbound_interfaces'] = $pfb['outbound_interfaces_float']; + } else { + $base_rule = $base_rule_reg; + $pfb['outbound_interfaces'] = explode(",",$pfb['config']['outbound_interface']); + // If OpenVPN Interfaces are not in dropdown menu + if ($pfb['openvpn'] == "on" && $config['openvpn']['openvpn-server'] || $pfb['openvpn'] == "on" && $config['openvpn']['openvpn-client']) + if (!in_array("openvpn",$sort_interfaces)) + array_push($pfb['outbound_interfaces'], "openvpn"); + } + } else { + # Define Empty Variable/Array + $pfb['outbound_interfaces_float'] = ""; + $pfb['outbound_interfaces'] = array(); + } + + + ################################################# + # Clear Removed Lists from Masterfiles # + ################################################# + + # Process to keep Masterfiles in Sync with Valid Lists from config.conf file. + $pfb['sync_master'] = TRUE; + + # Don't execute this function when pfBlockerNG is Disabled and 'Keep Blocklists' is enabled. + if ($pfb['enable'] == "" && $pfb['keep'] == "on") + $pfb['sync_master'] = FALSE; + + if ($pfb['sync_master']) { + $pfb['existing']['match']['type'] = "match"; + $pfb['existing']['permit']['type'] = "permit"; + $pfb['existing']['deny']['type'] = "deny"; + $pfb['existing']['native']['type'] = "native"; + $pfb['existing']['match']['folder'] = "{$pfb['matchdir']}"; + $pfb['existing']['permit']['folder'] = "{$pfb['permitdir']}"; + $pfb['existing']['deny']['folder'] = "{$pfb['denydir']}"; + $pfb['existing']['native']['folder'] = "{$pfb['nativedir']}"; + $pfb['actual']['match']['type'] = "match"; + $pfb['actual']['permit']['type'] = "permit"; + $pfb['actual']['deny']['type'] = "deny"; + $pfb['actual']['native']['type'] = "native"; + $pfb['actual']['match']['folder'] = "{$pfb['matchdir']}"; + $pfb['actual']['permit']['folder'] = "{$pfb['permitdir']}"; + $pfb['actual']['deny']['folder'] = "{$pfb['denydir']}"; + $pfb['actual']['native']['folder'] = "{$pfb['nativedir']}"; + + // Find all Enabled Continents Lists + foreach ($continents as $continent => $pfb_alias) { + if (is_array($config['installedpackages']['pfblockerng' . strtolower(preg_replace('/ /','',$continent))]['config']) && $pfb['enable'] == "on") { + $continent_config = $config['installedpackages']['pfblockerng' . strtolower(preg_replace('/ /','',$continent))]['config'][0]; + if ($continent_config['action'] != "Disabled") { + $cont_type = array ("countries4" => "_v4", "countries6" => "_v6"); + foreach ($cont_type as $c_type => $vtype) { + if ($continent_config[$c_type] != "") { + # Set Parameters for 'Match', 'Permit', 'Native' and 'Deny' + if (in_array($continent_config['action'],array('Match_Both','Match_Inbound','Match_Outbound','Alias_Match'))) { + $pfb['existing']['match'][] = "{$pfb_alias}{$vtype}"; + } elseif (in_array($continent_config['action'],array('Permit_Both','Permit_Inbound','Permit_Outbound','Alias_Permit'))){ + $pfb['existing']['permit'][] = "{$pfb_alias}{$vtype}"; + } elseif ($continent_config['action'] == "Alias_Native") { + $pfb['existing']['native'][] = "{$pfb_alias}{$vtype}"; + } else { + $pfb['existing']['deny'][] = "{$pfb_alias}{$vtype},"; // Add Trailing ',' + } + } + } + } + } + } + + # Find all Enabled IPv4/IPv6 Lists + $list_type = array ("pfblockernglistsv4" => "_v4", "pfblockernglistsv6" => "_v6"); + foreach ($list_type as $ip_type => $vtype) { + if ($config['installedpackages'][$ip_type]['config'] != "" && $pfb['enable'] == "on") { + foreach ($config['installedpackages'][$ip_type]['config'] as $list) { + if (is_array($list['row']) && $list['action'] != "Disabled") { + foreach ($list['row'] as $row) { + if ($vtype == "_v4") { + $pfb_alias = "{$row['header']}"; + } else { + $pfb_alias = "{$row['header']}_v6"; + } + # Collect Enabled Lists + if ($row['url'] != "" && $row['state'] != "Disabled") { + # Set Parameters for 'Match', 'Permit', 'Native' and 'Deny' + if (in_array($list['action'],array('Match_Both','Match_Inbound','Match_Outbound','Alias_Match'))) { + $pfb['existing']['match'][] = "{$pfb_alias}"; + } elseif (in_array($list['action'],array('Permit_Both','Permit_Inbound','Permit_Outbound','Alias_Permit'))) { + $pfb['existing']['permit'][] = "{$pfb_alias}"; + } elseif ($list['action'] == "Alias_Native") { + $pfb['existing']['native'][] = "{$pfb_alias}"; + } else { + $pfb['existing']['deny'][] = "{$pfb_alias},"; // Add Trailing ',' + } + } + } + } + } + } + } + + # Find all Enabled IPv4 'Custom List' Header Names and Check if 'Emerging Threats Update' and 'Custom List Update' Needs Force Updating + $list_type = array ("pfblockernglistsv4" => "_v4", "pfblockernglistsv6" => "_v6"); + foreach ($list_type as $ip_type => $vtype) { + if ($config['installedpackages'][$ip_type]['config'] != "" && $pfb['enable'] == "on") { + $count = -1; + foreach ($config['installedpackages'][$ip_type]['config'] as $list) { + if (is_array($list['row']) && $list['action'] != "Disabled") { + $count++; + # Check if 'Emerging Threats Update' Needs Updating before next CRON Event. + if (is_array($list['row']) && $row['state'] != "Disabled" && $pfb['etupdate'] == "enabled" && $vtype == "_v4") { + foreach ($list['row'] as $row) { + $aliasname = $row['header']; + if ($row['format'] == "et") { + unlink_if_exists("{$pfb['denydir']}/{$aliasname}.txt"); + $config['installedpackages']['pfblockerngreputation']['config'][0]['et_update'] = "disabled"; + break; + } + } + } + } + + # Collect Enabled Custom List Box Aliases + if (pfbng_text_area_decode($list['custom']) != "") { + if ($vtype == "_v4") { + $pfb_alias = "{$list['aliasname']}_custom"; + } else { + $pfb_alias = "{$list['aliasname']}_custom_v6"; + } + # Determine Folder Location for 'List' + if (in_array($list['action'],array('Match_Both','Match_Inbound','Match_Outbound','Alias_Match'))) { + $pfb['existing']['match'][] = "{$pfb_alias}"; + $pfbfolder = "{$pfb['matchdir']}"; + } elseif (in_array($list['action'],array('Permit_Both','Permit_Inbound','Permit_Outbound','Alias_Permit'))) { + $pfb['existing']['permit'][] = "{$pfb_alias}"; + $pfbfolder = "{$pfb['permitdir']}"; + } elseif ($list['action'] == "Alias_Native") { + $pfb['existing']['native'][] = "{$pfb_alias}"; + $pfbfolder = "{$pfb['nativedir']}"; + } else { + $pfb['existing']['deny'][] = "{$pfb_alias},"; // Add Trailing ',' + $pfbfolder = "{$pfb['denydir']}"; + } + # Determine if 'Custom List' Needs Force Updating before next CRON Event. + if ($list['custom_update'] == "enabled") { + unlink_if_exists("{$pfbfolder}/{$pfb_alias}.txt"); + # Uncheck 'Enabled' in List 'Custom_update' Setting + $config['installedpackages'][$ip_type]['config'][$count]['custom_update'] = "disabled"; + } + } + } + } + } + + # Collect all .txt file Names for each List Type + $list_types = array('match' => $pfb['matchdir'], 'permit' => $pfb['permitdir'], 'deny' => $pfb['denydir'], 'native' => $pfb['nativedir']); + foreach ($list_types as $type => $pfbfolder) { + $pfb_files = glob("$pfbfolder/*.txt"); + foreach ($pfb_files as $pfb_list) { + $pfb_file = basename($pfb_list,".txt"); + if ($type == "deny") { + $pfb['actual'][$type][] = "{$pfb_file},"; // Add Trailing ',' + } else { + $pfb['actual'][$type][] = "{$pfb_file}"; + } + } + } + + # Flag to execute pfctl and Rules Ordering + $pfb['remove'] = FALSE; + # Execute Final Summary as a List was Removed + $pfb['summary'] = FALSE; + + # Process to Remove Lists from Masterfile/DB Folder if they do not Exist + if (isset($pfb['existing'])) { + foreach ($pfb['existing'] as $pfb_exist) { + $existing_type = $pfb_exist['type']; + $pfbfolder = $pfb_exist['folder']; + foreach ($pfb['actual'] as $pfb_act) { + $actual_type = $pfb_act['type']; + if ($existing_type == $actual_type) { + switch ($existing_type) { + case "deny": + $results = array_diff($pfb_act, $pfb_exist); + $f_result = implode($results); + if ($f_result != "") { + $log = "[ Removing List(s) : {$f_result} ]\n"; + pfb_logger("{$log}","1"); + # Script to Remove un-associated Lists + exec ("{$pfb['script']} remove x x x {$f_result} >> {$pfb['log']} 2>&1"); + $pfb['summary'] = TRUE; + $pfb['remove'] = TRUE; + } + break; + case "match": + case "permit": + case "native": + $results = array_diff($pfb_act, $pfb_exist); + # This variable ($f_result) used in next section below. + $f_result = implode($results); + if (!empty($results)) { + foreach ($results as $pfb_results) { + $log = "[ Removing List(s) : {$pfb_results} ]\n"; + pfb_logger("{$log}","1"); + unlink_if_exists("{$pfbfolder}/{$pfb_results}.txt"); + } + $pfb['summary'] = TRUE; + $pfb['remove'] = TRUE; + } + break; + } + + # Allow Rebuilding of Changed Aliase to purge 'SKIP' Lists (when pfBlockerNG is Enabled) + $list_type = array ("pfblockernglistsv4" => "_v4", "pfblockernglistsv6" => "_v6"); + foreach ($list_type as $ip_type => $vtype) { + if ($f_result != "" && $pfb['enable'] == "on") { + foreach ($results as $removed_header) { + if ($config['installedpackages'][$ip_type]['config'] != "" && $pfb['enable'] == "on") { + foreach ($config['installedpackages'][$ip_type]['config'] as $list) { + $alias = "pfB_" . preg_replace("/\W/","",$list['aliasname']); + if (is_array($list['row'])) { + foreach ($list['row'] as $row) { + $removed = rtrim($removed_header, ','); + if ($row['header'] == $removed) { + $pfb['summary'] = TRUE; + $pfb['remove'] = TRUE; + # Add Alias to Update Array + $pfb_alias_lists[] = "{$alias}"; + $pfb_alias_lists_all[] = "{$alias}"; + } + } + } + } + } + } + } + } + } + } + } + } + } + + ######################################################### + # Clear Match/Pass/ET/Original Files/Folders # + ######################################################### + + # When pfBlockerNG is Disabled and 'Keep Blocklists' is Disabled. + if ($pfb['enable'] == "" && $pfb['keep'] == "" && !$pfb['install']) { + $log = "\n Removing DB Files/Folders \n"; + pfb_logger("{$log}","1"); + + unlink_if_exists("{$pfb['dbdir']}/masterfile"); + unlink_if_exists("{$pfb['dbdir']}/mastercat"); + unlink_if_exists("{$pfb['supptxt']}"); + rmdir_recursive("{$pfb['origdir']}"); + rmdir_recursive("{$pfb['matchdir']}"); + rmdir_recursive("{$pfb['permitdir']}"); + rmdir_recursive("{$pfb['denydir']}"); + rmdir_recursive("{$pfb['nativedir']}"); + rmdir_recursive("{$pfb['etdir']}"); + } + + + ######################################### + # Create Suppression Txt File # + ######################################### + + if ($pfb['enable'] == "on" && $pfb['supp'] == "on") + pfb_create_suppression_file(); + + + ################################# + # Assign Countries # + ################################# + + foreach ($continents as $continent => $pfb_alias) { + if (is_array($config['installedpackages']['pfblockerng' . strtolower(preg_replace('/ /','',$continent))]['config'])) { + $continent_config = $config['installedpackages']['pfblockerng' . strtolower(preg_replace('/ /','',$continent))]['config'][0]; + if ($continent_config['action'] != "Disabled" && $pfb['enable'] == "on") { + + # Determine Folder Location for Alias (return array $pfbarr) + pfb_determine_list_detail($continent_config['action']); + $pfb['skip'] = $pfbarr['skip']; + $pfb_descr = $pfbarr['descr']; + $pfbfolder = $pfbarr['folder']; + + // Determine if Continent Lists require Action (IPv4 and IPv6) + $cont_type = array ("countries4" => "_v4", "countries6" => "_v6"); + foreach ($cont_type as $c_type => $vtype) { + + $continent = ""; + if ($continent_config[$c_type] != "") { + + // Collect Selected ISO Country Files + foreach (explode(",", $continent_config[$c_type]) as $iso) { + if ($iso != "" && file_exists($pfb['ccdir'] .'/' . $iso . $vtype . '.txt')) { + $continent .= file_get_contents ($pfb['ccdir'] . '/' . $iso . $vtype . '.txt'); + } + } + + if (file_exists($pfb['origdir'] . '/' . $pfb_alias . $vtype . '.orig')) + $continent_existing = preg_replace('/\s/', '', file ($pfb['origdir'] . '/' . $pfb_alias . $vtype . '.orig')); + + // Collect New Continent Data for comparison. Cleanup Array for Comparison + $continent_new = preg_split ('/$\R?^/m', $continent); + $line = count ( $continent_new ) - 1; + $match = $continent_new[$line]; + $continent_new[$line] = rtrim($match, "\n"); + + # Check if pfBlockerNG pfctl Continent Tables are Empty (pfBlockerNG was Disabled w/ "keep", then Re-enabled) + $pfctlck = exec ("/sbin/pfctl -vvsTables | grep -A1 {$pfb_alias}{$vtype} | awk '/Addresses/ {s+=$2}; END {print s}'"); + if (empty($pfctlck) && file_exists($pfbfolder . '/' . $pfb_alias . $vtype . '.txt')) { + $file_cont = file_get_contents($pfbfolder . '/' . $pfb_alias . $vtype . '.txt'); + @file_put_contents($pfb['aliasdir'] . '/' . $pfb_alias . $vtype . '.txt',$file_cont, LOCK_EX); + # PFCTL - Update Only Aliases that have been updated. ('Reputation' Disabled) + $pfb_alias_lists[] = "{$pfb_alias}{$vtype}"; + } + + # Collect Active Alias Lists (Used for pfctl Update when 'Reputation' is enabled). + $pfb_alias_lists_all[] = "{$pfb_alias}{$vtype}"; + + // Compare Existing (Original File) and New Continent Data + if ($continent_new === $continent_existing && !empty($pfctlck) && file_exists($pfbfolder . '/' . $pfb_alias . $vtype . '.txt') && $pfb['reuse'] == "") { + # Format Log into clean Tab Spaces + $string_final = "{$pfb_alias}{$vtype}"; + if (strlen($string_final) > 10) { + $log_tab = "\t"; + } else { + $log_tab = "\t\t"; + } + + if (!$pfb['save']) { + $log = "\n[ {$pfb_alias}{$vtype} ] {$log_tab} exists, Reloading File [ NOW ]\n"; + pfb_logger("{$log}","1"); + } + } else { + // Do not proceed with Changes on User 'Save' + if (!$pfb['save']) { + $log = "\n[ {$pfb_alias}{$vtype} ] {$log_tab} Changes Found... Updating \n"; + pfb_logger("{$log}","1"); + + # Test to Skip d-dup and p-dup functions when changes are found. + $pfb['dupcheck'] = TRUE; + + $pfb_alias_lists[] = "{$pfb_alias}{$vtype}"; + + // Script to call Duplication Check Process only on IPv4 + if ($pfb['dup'] == "on" && $pfb['skip'] && $vtype == "_v4") { + // Copy Continent Data to 'lists' folder for duplication processing + @file_put_contents($pfb['origdir'] . '/' . $pfb_alias . $vtype . '.orig',$continent, LOCK_EX); + @file_put_contents($pfb['denydir'] . '/' . $pfb_alias . $vtype . '.txt',$continent, LOCK_EX); + exec ("{$pfb['script']} continent {$pfb_alias}{$vtype} >> {$pfb['log']} 2>&1"); + $continent = file_get_contents($pfbfolder . '/' . $pfb_alias . $vtype . '.txt'); + @file_put_contents($pfb['aliasdir'] . '/' . $pfb_alias . $vtype . '.txt',$continent, LOCK_EX); + } else { + @file_put_contents($pfbfolder . '/' . $pfb_alias . $vtype . '.txt',$continent, LOCK_EX); + @file_put_contents($pfb['origdir'] . '/' . $pfb_alias . $vtype . '.orig',$continent, LOCK_EX); + @file_put_contents($pfb['aliasdir'] . '/' . $pfb_alias . $vtype . '.txt',$continent, LOCK_EX); + } + + # Check if File Exists and is >0 in Size and Save alias file + $file_chk = "0"; + $cont_chk = "{$pfbfolder}/{$pfb_alias}{$vtype}.txt"; + if (file_exists($cont_chk) && @filesize($cont_chk) >0) + $file_chk = exec ("/usr/bin/grep -cv '^#\|^$' {$cont_chk}"); + + if ($file_chk == "0" || $file_chk == "1") { + $new_file = "1.1.1.1\n"; + @file_put_contents($pfbfolder . '/' . $pfb_alias . $vtype . '.txt', $new_file, LOCK_EX); + @file_put_contents($pfb['aliasdir'] . "/" . $pfb_alias . $vtype . ".txt", $new_file, LOCK_EX); + $log = "[ {$pfb_alias}{$vtype} ] Found no Unique IPs, Adding '1.1.1.1' to avoid Empty File\n"; + pfb_logger("{$log}","1"); + } + } + } + + + if (file_exists($pfbfolder . '/' . $pfb_alias . $vtype . '.txt')) { + #Create alias config + $new_aliases_list[] = "{$pfb_alias}{$vtype}"; + + $pfb_contlog = $continent_config['aliaslog']; + + $new_aliases[] = array( "name" => "{$pfb_alias}{$vtype}", + "url" => "{$pfb['weblocal']}?pfb={$pfb_alias}{$vtype}", + "updatefreq" => "32", + "address" => "", + "descr" => "pfBlockerNG {$vtype} {$pfb_descr} Country Alias", + "type" => "urltable", + "detail" => "DO NOT EDIT THIS ALIAS" + ); + + #Create rule if action permits + switch ($continent_config['action']) { + case "Deny_Both": + case "Deny_Outbound": + $rule = $base_rule; + $rule['type'] = "{$pfb['deny_action_outbound']}"; + if ($vtype == "_v6") + $rule['ipprotocol'] = "inet6"; + if ($pfb['float'] == "on") + $rule['direction'] = "any"; + $rule['descr']= "{$pfb_alias}{$vtype}{$pfb['suffix']}"; + $rule['source'] = array("any" => ""); + $rule['destination'] = array ("address" => "{$pfb_alias}{$vtype}"); + if ($pfb['config']['enable_log'] == "on" || $pfb_contlog == "enabled") + $rule['log'] = ""; + $deny_outbound[] = $rule; + if ($continent_config['action'] != "Deny_Both") + break; + case "Deny_Inbound": + $rule = $base_rule; + $rule['type'] = "{$pfb['deny_action_inbound']}"; + if ($vtype == "_v6") + $rule['ipprotocol'] = "inet6"; + if ($pfb['float'] == "on") + $rule['direction'] = "any"; + $rule['descr'] = "{$pfb_alias}{$vtype}{$pfb['suffix']}"; + $rule['source'] = array("address" => "{$pfb_alias}{$vtype}"); + $rule['destination'] = array ("any" => ""); + if ($pfb['config']['enable_log'] == "on" || $pfb_contlog == "enabled") + $rule['log'] = ""; + $deny_inbound[] = $rule; + break; + case "Permit_Both": + case "Permit_Outbound": + $rule = $base_rule; + $rule['type'] = "pass"; + if ($vtype == "_v6") + $rule['ipprotocol'] = "inet6"; + if ($pfb['float'] == "on") + $rule['direction'] = "any"; + $rule['descr'] = "{$pfb_alias}{$vtype}{$pfb['suffix']}"; + $rule['source'] = array ("any" => ""); + $rule['destination'] = array("address" => "{$pfb_alias}{$vtype}"); + if ($pfb['config']['enable_log'] == "on" || $pfb_contlog == "enabled") + $rule['log'] = ""; + $permit_outbound[] = $rule; + if ($continent_config['action'] != "Permit_Both") + break; + case "Permit_Inbound": + $rule = $base_rule; + $rule['type'] = "pass"; + if ($vtype == "_v6") + $rule['ipprotocol'] = "inet6"; + if ($pfb['float'] == "on") + $rule['direction'] = "any"; + $rule['descr'] = "{$pfb_alias}{$vtype}{$pfb['suffix']}"; + $rule['source'] = array("address"=> "{$pfb_alias}{$vtype}"); + $rule['destination'] = array ("any" => ""); + if ($pfb['config']['enable_log'] == "on" || $pfb_contlog == "enabled") + $rule['log'] = ""; + $permit_inbound[] = $rule; + break; + case "Match_Both": + case "Match_Outbound": + $rule = $base_rule_float; + $rule['type'] = "match"; + if ($vtype == "_v6") + $rule['ipprotocol'] = "inet6"; + $rule['direction'] = "any"; + $rule['descr'] = "{$pfb_alias}{$vtype}{$pfb['suffix']}"; + $rule['source'] = array ("any" => ""); + $rule['destination'] = array ("address" => "{$pfb_alias}{$vtype}"); + if ($pfb['config']['enable_log'] == "on" || $pfb_contlog == "enabled") + $rule['log'] = ""; + $match_outbound[] = $rule; + if ($list['action'] != "Match_Both") + break; + case "Match_Inbound": + $rule = $base_rule_float; + $rule['type'] = "match"; + if ($vtype == "_v6") + $rule['ipprotocol'] = "inet6"; + $rule['direction'] = "any"; + $rule['descr'] = "{$pfb_alias}{$vtype}{$pfb['suffix']}"; + $rule['source'] = array ("address" => "{$pfb_alias}{$vtype}"); + $rule['destination'] = array ( "any" => ""); + if ($pfb['config']['enable_log'] == "on" || $pfb_contlog == "enabled") + $rule['log'] = ""; + $match_inbound[] = $rule; + break; + } + } else { + #unlink continent list if any + unlink_if_exists($pfb['aliasdir'] . '/' . $pfb_alias . $vtype . '.txt'); + } + } + } + } + #mark pfctl aliastable for cleanup + if (!in_array($pfb_alias, $aliases_list)) { + $aliases_list[] = "{$pfb_alias}{$vtype}"; + } + } + } + # UNSET variables + unset ($continent, $continent_existing, $continent_new); + + ################################################# + # Download and Collect IPv4/IPv6 lists # + ################################################# + + # IPv4 REGEX Definitions + $pfb['range'] = '/((?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))-((?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))/'; + $pfb['block'] = '/(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[ 0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.([0]{1})\s+/'; + $pfb['cidr'] = '/((?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)?\/[0-9]{2})/'; + $pfb['single'] = '/(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\s+/'; + $pfb['s_html'] = '/(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)/'; + + # IPv4 preg_replace Regex Filter array + $pfb_ipreg = array(); + $pfb_ipreg[0] = '/\b0+(?=\d)/'; # Remove any Leading Zeros in each Octet + $pfb_ipreg[1] = '/\s/'; # Remove any Whitespaces + $pfb_ipreg[2] = '/127\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/'; # Remove any Loopback Addresses 127/8 + $pfb_ipreg[3] = '/0\.0\.0\.0\/32/'; # Remove 0.0.0.0/32 + $pfb_ipreg[4] = '/0\.0\.0\.0/'; # Remove 0.0.0.0 + + # IPv6 REGEX Definitions -- ** Still Needs some Adjustment on Regex Definition for IPv6 ** + # https://mebsd.com/coding-snipits/php-regex-ipv6-with-preg_match.html + $pattern1 = '([A-Fa-f0-9]{1,4}:){7}[A-Fa-f0-9]{1,4}'; + $pattern2 = '[A-Fa-f0-9]{1,4}::([A-Fa-f0-9]{1,4}:){0,5}[A-Fa-f0-9]{1,4}'; + $pattern3 = '([A-Fa-f0-9]{1,4}:){2}:([A-Fa-f0-9]{1,4}:){0,4}[A-Fa-f0-9]{1,4}'; + $pattern4 = '([A-Fa-f0-9]{1,4}:){3}:([A-Fa-f0-9]{1,4}:){0,3}[A-Fa-f0-9]{1,4}'; + $pattern5 = '([A-Fa-f0-9]{1,4}:){4}:([A-Fa-f0-9]{1,4}:){0,2}[A-Fa-f0-9]{1,4}'; + $pattern6 = '([A-Fa-f0-9]{1,4}:){5}:([A-Fa-f0-9]{1,4}:){0,1}[A-Fa-f0-9]{1,4}'; + $pattern7 = '([A-Fa-f0-9]{1,4}:){6}:[A-Fa-f0-9]{1,4}'; + $pattern8 = '[A-Fa-f0-9]{1,4}:[A-Fa-f0-9]{1,4}:[A-Fa-f0-9]{1,4}::\/[0-9]{2}'; + $pattern9 = '[A-Fa-f0-9]{1,4}:([A-Fa-f0-9]{1,4}::)\/[0-9]{2}'; + $pattern10 = '[A-Fa-f0-9]{1,4}::\/[0-9]{2}'; + $pfb['ipv6'] = "/($pattern1)|($pattern2)|($pattern3)|($pattern4)|($pattern5)|($pattern6)|($pattern7)|($pattern8)|($pattern9)|($pattern10)/"; + + $pfb['supp_update'] = FALSE; + $list_type = array ("pfblockernglistsv4" => "_v4", "pfblockernglistsv6" => "_v6"); + foreach ($list_type as $ip_type => $vtype) { + if ($config['installedpackages'][$ip_type]['config'] != "") { + foreach ($config['installedpackages'][$ip_type]['config'] as $list) { + if ($list['action'] != "Disabled" && $pfb['enable'] == "on" && !$pfb['save'] && is_array($list['row'])) { + # Capture Alias Name + $alias = "pfB_" . preg_replace("/\W/","",$list['aliasname']); + foreach ($list['row'] as $row) { + if ($row['url'] != "" && $row['state'] != "Disabled") { + + # Determine Folder Location for Alias (return array $pfbarr) + pfb_determine_list_detail($list['action']); + $pfb['skip'] = $pfbarr['skip']; + $pfbfolder = $pfbarr['folder']; + + if ($vtype == "_v4") { + $header_url = "{$row['header']}"; + } else { + $header_url = "{$row['header']}_v6"; + } + + # Format Log into clean Tab Spaces + if (strlen($header_url) > 10) { + $log_tab = "\t"; + } else { + $log_tab = "\t\t"; + } + + // Empty Header Field Validation Check + if (empty($header_url) || preg_match("/\W/",$header_url)) { + $log = "\n [ {$row['url']} ]\n ** TERMINATED - Header contains Blank/International/Special or Spaces\n"; + pfb_logger("{$log}","2"); + continue; + } + + # Collect Active Alias List (Used for pfctl Update when 'Reputation' is enabled. + $pfb_alias_lists_all[] = "{$alias}"; + + if (file_exists($pfbfolder . '/' . $header_url . '.txt') && $pfb['reuse'] == "") { + if ($row['state'] == "Hold") { + $log = "\n[ {$header_url} ] {$log_tab} Static Hold [ NOW ]\n"; + } else { + $log = "\n[ {$header_url} ] {$log_tab} exists, Reloading File [ NOW ]\n"; + } + pfb_logger("{$log}","1"); + } else { + if ($pfb['reuse'] == "on" && file_exists($pfb['origdir'] . '/' . $header_url . '.orig')) { + $log = "\n[ {$header_url} ] {$log_tab} Using Previously Downloaded File [ NOW ]\n"; + } else { + $log = "\n[ {$header_url} ] {$log_tab} Downloading New File [ NOW ]\n"; + } + pfb_logger("{$log}","1"); + + # Perform Remote URL Date/Time Stamp checks + $host = @parse_url($row['url']); + $list_url = "{$row['url']}"; + if ($row['format'] != "rsync" || $row['format'] != "html") { + if ($host['host'] == "127.0.0.1" || $host['host'] == $pfb['iplocal'] || empty($host['host'])) { + $remote_tds = "local"; + } else { + $remote_tds = @implode(preg_grep("/Last-Modified/", get_headers($list_url))); + $remote_tds = preg_replace("/^Last-Modified: /","", $remote_tds); + } + } + + $url_list = array(); + if ($row['format'] == "gz" || $row['format'] == "gz_2") { + $file_dwn = "{$pfb['origdir']}/{$header_url}.gz"; + if ($pfb['reuse'] == "on" && file_exists($file_dwn)) { + # File Exists/Reuse + } else { + $url_gz = "{$row['url']}"; + $file_gz = @file_get_contents($url_gz); + @file_put_contents($file_dwn, $file_gz, LOCK_EX); + if ($remote_tds == "local") + $remote_tds = gmdate ("D, d M Y H:i:s T", filemtime($file_dwn)); + $remote_stamp = strtotime($remote_tds); + if (!empty($remote_stamp) && file_exists($file_dwn)) + touch ($file_dwn, $remote_stamp); + } + $url_list = @gzfile($file_dwn); + } + + # IBlock Large Files mixed with IPs and Domains. PHP mem of 256M can't handle very large Files. + if ($row['format'] == "gz_lg") { + $file_dwn = "{$pfb['origdir']}/{$header_url}.gz"; + if ($pfb['reuse'] == "on" && file_exists($file_dwn)) { + # File Exists/Reuse + } else { + $url_gz = "{$row['url']}"; + $file_gz = @file_get_contents($url_gz); + @file_put_contents($file_dwn, $file_gz, LOCK_EX); + exec ("/usr/bin/gunzip -c {$file_dwn} | /usr/bin/sed 's/^.*://' | /usr/bin/grep -v '[a-zA-Z]\|^$\|^#' > {$pfb['origdir']}/{$header_url}.orig"); + if ($remote_tds == "local") + $remote_tds = gmdate ("D, d M Y H:i:s T", filemtime($file_dwn)); + $remote_stamp = strtotime($remote_tds); + if (!empty($remote_stamp) && file_exists($file_dwn)) + touch ($file_dwn, $remote_stamp); + } + $url_list = @file($pfb['origdir'] . '/' . $header_url . '.orig'); + } + + elseif ($row['format'] == "zip") { + $file_dwn = "{$pfb['origdir']}/{$header_url}.zip"; + if ($pfb['reuse'] == "on" && file_exists($file_dwn)) { + # File Exists/Reuse + } else { + $url_zip = "{$row['url']}"; + if (!$file_zip = @file_get_contents($url_zip)) { + $error = error_get_last(); + $log = "\n [ {$header_url} ] {$error['message']} \n"; + pfb_logger("{$log}","2"); + } else { + @file_put_contents($file_dwn, $file_zip, LOCK_EX); + if ($remote_tds == "local") + $remote_tds = gmdate ("D, d M Y H:i:s T", filemtime($file_dwn)); + $remote_stamp = strtotime($remote_tds); + if (!empty($remote_stamp) && file_exists($file_dwn)) + touch ($file_dwn, $remote_stamp); + } + } + $zip_out = "{$pfb['origdir']}/{$header_url}.orig"; + exec ("/usr/bin/tar -xOf {$file_dwn} | tr ',' '\n' > {$zip_out}"); + $url_list = @file($zip_out); + } + + elseif ($row['format'] == "et") { + $file_dwn = "{$pfb['origdir']}/{$header_url}.gz"; + # Script to Call ET IQRISK Process + if ($pfb['reuse'] == "on" && file_exists($file_dwn)) { + # File Exists/Reuse + } else { + $url_et = "{$row['url']}"; + $file_et = @file_get_contents($url_et); + @file_put_contents($file_dwn, $file_et, LOCK_EX); + if ($remote_tds == "local") + $remote_tds = gmdate ("D, d M Y H:i:s T", filemtime($file_dwn)); + $remote_stamp = strtotime($remote_tds); + if (!empty($remote_stamp) && file_exists($file_dwn)) + touch ($file_dwn, $remote_stamp); + } + exec ("{$pfb['script']} et {$header_url} x x x x x {$pfb['etblock']} {$pfb['etmatch']} >> {$pfb['log']} 2>&1"); + $url_list = @file($pfb['origdir'] . '/' . $header_url . '.orig'); + } + + elseif ($row['format'] == "xlsx") { + $file_dwn = "{$pfb['origdir']}/{$header_url}.zip"; + # Script to Call XLSX Process + if ($pfb['reuse'] == "on" && file_exists($file_dwn)) { + # File Exists/Reuse + } else { + $url_xlsx = "{$row['url']}"; + $file_xlsx = @file_get_contents($url_xlsx); + @file_put_contents($file_dwn, $file_xlsx, LOCK_EX); + if ($remote_tds == "local") + $remote_tds = gmdate ("D, d M Y H:i:s T", filemtime($file_dwn)); + $remote_stamp = strtotime($remote_tds); + if (!empty($remote_stamp) && file_exists($file_dwn)) + touch ($file_dwn, $remote_stamp); + } + exec ("{$pfb['script']} xlsx {$header_url} >> {$pfb['log']} 2>&1"); + $url_list = @file($pfb['origdir'] . '/' . $header_url . '.orig'); + } + + elseif ($row['format'] == "txt") { + $file_dwn = "{$pfb['origdir']}/{$header_url}.orig"; + if ($pfb['reuse'] == "on" && file_exists($file_dwn)) { + $url_list = @file($file_dwn); + } else { + $url_other = @file($row['url']); + $url_list = $url_other; + @file_put_contents($file_dwn, $url_other, LOCK_EX); + if ($remote_tds == "local") + $remote_tds = gmdate ("D, d M Y H:i:s T", filemtime($file_dwn)); + $remote_stamp = strtotime($remote_tds); + if (!empty($remote_stamp) && file_exists($file_dwn)) + touch ($file_dwn, $remote_stamp); + } + } + + elseif ($row['format'] == "html" || $row['format'] == "block") { + $file_dwn = "{$pfb['origdir']}/{$header_url}.raw"; + if ($pfb['reuse'] == "on" && file_exists($file_dwn)) { + # File Exists/Reuse + $return = 0; + } else { + $url_html = "{$row['url']}"; + exec ("/usr/bin/fetch -v -o {$file_dwn} -T 20 {$url_html}",$output,$return); + } + if ($return == 0) + $url_list = @file($file_dwn); + } + + elseif ($row['format'] == "rsync") { + $file_dwn = "{$pfb['origdir']}/{$header_url}.orig"; + if ($pfb['reuse'] == "on" && file_exists($file_dwn)) { + # File Exists/Reuse + } else { + $url_rsync = "{$row['url']}"; + exec ("/usr/local/bin/rsync --timeout=5 {$url_rsync} {$file_dwn}"); + } + $url_list = @file($file_dwn); + } + + #extract range lists + $new_file = ""; + if (!empty($url_list)) { + if ($row['format'] == "gz" && $vtype == "_v4") { + foreach ($url_list as $line) { + if (!preg_match("/^#/", $line)) { + # Network range 192.168.0.0-192.168.0.254 + if (preg_match($pfb['range'],$line,$matches)) { + $a_cidr = ip_range_to_subnet_array_temp2($matches[1],$matches[2]); + if (!empty($a_cidr)) { + foreach ($a_cidr as $cidr) { + $new_file .= preg_replace($pfb_ipreg,'',$cidr) . "\n"; + } + } + } + } + } + } + + elseif ($row['format'] == "block" && $vtype == "_v4") { + foreach ($url_list as $line) { + if (!preg_match("/^#/", $line)) { + # Block Type '218.77.79.0 218.77.79.255 24' + if (preg_match($pfb['block'],$line,$matches)) { + $new_file .= preg_replace($pfb_ipreg, '',$matches[0]) . "/24\n"; + } + } + } + } + + elseif ($row['format'] == "html" && $vtype == "_v4") { + foreach ($url_list as $line) { + if (!preg_match("/^#/", $line)) { + # CIDR format 192.168.0.0/16 + if (preg_match($pfb['cidr'],$line,$matches)) { + $new_file .= preg_replace($pfb_ipreg, '',$matches[0]) . "\n"; + } + # Single ip addresses + elseif (preg_match($pfb['s_html'],$line,$matches)) { + $new_file .= preg_replace($pfb_ipreg, '',$matches[0]) . "\n"; + } + } + } + } + + elseif ($vtype == "_v6") { + foreach ($url_list as $line) { + if (!preg_match("/^#/", $line)) { + # IPv6 Regex Match + if (preg_match($pfb['ipv6'],$line,$matches)) { + $new_file .= preg_replace($pfb_ipreg, '',$matches[0]) . "\n"; + } + } + } + } + + else { + foreach ($url_list as $line) { + if (!preg_match("/^#/", $line)) { + # CIDR format 192.168.0.0/16 + if (preg_match($pfb['cidr'],$line,$matches)) { + $new_file .= preg_replace($pfb_ipreg, '',$matches[0]) . "\n"; + } + # Single ip addresses + elseif (preg_match($pfb['single'],$line,$matches)) { + $new_file .= preg_replace($pfb_ipreg, '',$matches[0]) . "\n"; + } + } + } + } + } + + # Check to see if Blocklist actually Failed Download or has no IPs listed. + if ($row['format'] == "html" || $row['format'] == "block") { + $url_chk = $file_dwn; + } else { + $url_chk = "{$pfb['origdir']}/{$header_url}.orig"; + } + + # Check if File Exists and is >0 in Size + $file_chk = ""; + if (file_exists($url_chk) && @filesize($url_chk) >0) + $file_chk = exec ("/usr/bin/grep -cv '^#\|^$' {$url_chk}"); + + if ($file_chk == "0") { + $new_file = "1.1.1.1\n"; + $url_other = $new_file; + $log = "[ {$header_url} ] Found no IPs, Adding '1.1.1.1' to avoid Download FAIL\n"; + pfb_logger("{$log}","1"); + } + + if ($new_file != "") { + if ($row['format'] == "gz" || $row['format'] == "gz_2" || $row['format'] == "html" || $row['format'] == "block") { + # Re-Save these formats as original file + $url_other = $new_file; + @file_put_contents($pfb['origdir'] . '/' . $header_url . '.orig',$url_other, LOCK_EX); + } + + # Save List to '.txt' format in appropriate Folder + @file_put_contents($pfbfolder . '/' .$header_url . '.txt',$new_file, LOCK_EX); + + if ($pfb['rep'] == "on" && $pfb['skip'] && $vtype == "_v4") { + # Script to Call p24 Process + exec ("{$pfb['script']} p24 {$header_url} {$pfb['max']} {$pfb['dedup']} {$pfb['ccexclude']} {$pfb['ccwhite']} {$pfb['ccblack']} >> {$pfb['log']} 2>&1"); + } + + if ($pfb['dup'] == "on" && $pfb['skip'] && $vtype == "_v4") { + # Script to call Duplication Check Process + exec ("{$pfb['script']} duplicate {$header_url} >> {$pfb['log']} 2>&1"); + } + + # PFCTL - Update Only Aliases that have been updated only. + $pfb_alias_lists[] = "{$alias}"; + # Launch d-dup and p-dup functions when changes are found. + if ($pfb['skip'] && $vtype == "_v4") + $pfb['dupcheck'] = TRUE; + # Enable Suppression Process due to Updates + if ($pfb['supp'] == "on" && $vtype == "_v4") + $pfb['supp_update'] = TRUE; + + } else { + # Log FAILED Downloads and Check if Firewall or Snort/Suricata is Blocking Host + $log = "\n [ {$alias} {$header_url} ] Download FAIL [ NOW ]\n"; + pfb_logger("{$log}","2"); + + # Rebuild Previous List File from contents of Masterfile + if ($pfb['skip'] && $vtype == "_v4") { + # Search with trailing Whitespace to match exact Header in Masterfile + $header_url2 = $header_url . "[[:space:]]"; + $file_chk = exec ("/usr/bin/grep {$header_url2} {$pfb['master']} | grep -c ^"); + + if (!file_exists($pfbfolder . '/' . $header_url . '.txt') && @$file_chk > 0 && file_exists($pfb['master'])) { + $log = " [ {$alias} {$header_url} ] Found: {$file_chk} Line(s), Restoring previous List from Master \n"; + pfb_logger("{$log}","2"); + exec ("/usr/bin/grep {$header_url2} {$pfb['master']} | cut -d' ' -f2 > {$pfbfolder}/{$header_url}.txt"); + } + } + # A "Space" string Variable + $sp = " "; + $ip = @gethostbyname($host['host']); + $ip2 = preg_replace("/(\d{1,3})\.(\d{1,3}).(\d{1,3}).(\d{1,3})/", "\"^$1\.$2\.$3\.\"", $ip); + + # Only Perform these Checks if they are not "localfiles" + if ($host['host'] == "127.0.0.1" || $host['host'] == $pfb['iplocal'] || empty($host['host'])) { + $log = " [ {$alias} {$header_url} ] Local File Failure \n"; + pfb_logger("{$log}","2"); + } else { + # only perform these steps if an 'IP' is found. + if (!empty($ip)) { + // Query for Exact IP Match + $result_b1 = array(); + $pfb_b1 = exec ("/usr/bin/grep ^{$ip} {$pfbfolder}/*", $result_b1); + // Query for First Three IP Octet Matches + $result_b2 = array(); + $pfb_b2 = exec ("/usr/bin/grep {$ip2} {$pfbfolder}/*", $result_b2); + // Query Snort/Suricata snort2c IP Block Table + $snort_pfb = exec("/sbin/pfctl -t snort2c -T show | grep {$ip}"); + + # If an exact IP Match is not found report any First Three IP Octets. + if (!empty($result_b1)) { + $final_b1 = implode("\n ", $result_b1); + $log = " [ {$alias} {$header_url}, {$ip} ] Firewall IP Block Found in : \n{$sp}{$final_b1}\n"; + pfb_logger("{$log}","2"); + } else { + if (!empty($result_b2)) { + $final_b2 = implode("\n ", $result_b2); + $log = " [ {$alias} {$header_url}, {$ip} ] *Potential* Firewall IP Block Found in : \n{$sp}{$final_b2}\n"; + pfb_logger("{$log}","2"); + } + } + if (!empty($snort_pfb)) { + $log = " [ {$alias} {$header_url}, {$ip} ] snort2c IP Block Found in : [ {$snort_pfb} ]\n"; + pfb_logger("{$log}","2"); + } + } else { + $log = " [ {$alias} {$header_url} ] No host IP found \n"; + pfb_logger("{$log}","2"); + } + } + } + # UNSET variables + unset ($file_gz,$file_zip,$file_et,$file_xlsx,$url_other,$url_list); + } + } + } + #check custom network list + if (pfbng_text_area_decode($list['custom']) != "") { + + if ($vtype == "_v4") { + $aliascustom = "{$list['aliasname']}_custom"; + } else { + $aliascustom = "{$list['aliasname']}_custom_v6"; + } + + # Format Log into clean Tab Spaces + if (strlen($aliascustom) > 10) { + $log_tab = "\t"; + } else { + $log_tab = "\t\t"; + } + + # Collect Active Alias List (Used for pfctl Update when 'Reputation' is enabled. + $pfb_alias_lists_all[] = "{$alias}"; + + # Determine Folder Location for Alias (return array $pfbarr) + pfb_determine_list_detail($list['action']); + $pfb['skip'] = $pfbarr['skip']; + $pfbfolder = $pfbarr['folder']; + + if (file_exists($pfbfolder . '/' . $aliascustom . '.txt') && $pfb['reuse'] == "") { + $log = "\n[ {$aliascustom} ] {$log_tab} exists, Reloading File [ NOW ]\n"; + pfb_logger("{$log}","1"); + } else { + $url_list = array(); + $log = "\n[ {$aliascustom} ] {$log_tab} Loading Custom File [ NOW ]\n"; + pfb_logger("{$log}","1"); + + $custom_list = pfbng_text_area_decode($list['custom']) . "\n"; + @file_put_contents($pfb['origdir'] . '/' . $aliascustom . '.orig', $custom_list, LOCK_EX); + $url_list = @file($pfb['origdir'] . '/' . $aliascustom . '.orig'); + + $new_file = ""; + if (!empty($url_list)) { + foreach ($url_list as $line) { + if ($vtype == "_v4") { + # CIDR format 192.168.0.0/16 + if (preg_match($pfb['cidr'],$line,$matches)) { + $new_file .= preg_replace($pfb_ipreg, '',$matches[0]) . "\n"; + } + # Single ip addresses + elseif (preg_match($pfb['s_html'],$line,$matches)) { + $new_file .= preg_replace($pfb_ipreg, '',$matches[0]) . "\n"; + } + # Network range 192.168.0.0-192.168.0.254 + elseif (preg_match($pfb['range'],$line,$matches)) { + $a_cidr = ip_range_to_subnet_array_temp2($matches[1],$matches[2]); + if (!empty($a_cidr)) { + foreach ($a_cidr as $cidr) { + $new_file .= preg_replace($pfb_ipreg, '',$cidr) . "\n"; + } + } + } + } else { + # IPv6 Regex + if (preg_match($pfb['ipv6'],$line,$matches)) { + $new_file .= preg_replace($pfb_ipreg, '',$matches[0]) . "\n"; + } + } + } + + } + if ($new_file != "") { + # PFCTL - Collect Only Aliases that have been updated only. + $pfb_alias_lists[] = "{$alias}"; + # Collect Updated lists for Suppression Process + @file_put_contents($pfbfolder . '/'. $aliascustom . '.txt',$new_file, LOCK_EX); + # Enable Suppression Process due to Updates + if ($pfb['supp'] == "on" && $vtype == "_v4") + $pfb['supp_update'] = TRUE; + if ($pfb['rep'] == "on" && $pfb['skip'] && $vtype == "_v4") { + # Script to Call p24 Process + exec ("{$pfb['script']} p24 {$aliascustom} {$pfb['max']} {$pfb['dedup']} {$pfb['ccexclude']} {$pfb['ccwhite']} {$pfb['ccblack']} >> {$pfb['log']} 2>&1"); + } + if ($pfb['dup'] == "on" && $pfb['skip'] && $vtype == "_v4") { + # Script to call Duplication Check Process + exec ("{$pfb['script']} duplicate {$aliascustom} >> {$pfb['log']} 2>&1"); + } + } else { + $log = "[ {$aliascustom} ] Custom List Error ]\n"; + pfb_logger("{$log}","1"); + } + } + } + } + } + } + } + + + ################################# + # REPUTATION PROCESSES # + ################################# + + # IP Reputation processes (pdup and ddup) + if ($pfb['pdup'] == "on" && $pfb['dupcheck'] && !$pfb['save'] && $pfb['enable'] == "on") { + # Script to run pdup process + exec ("{$pfb['script']} pdup x {$pfb['pmax']} >> {$pfb['log']} 2>&1"); + } + if ($pfb['dedup'] == "on" && $pfb['dupcheck'] && !$pfb['save'] && $pfb['enable'] == "on") { + # Script to run dedup process + exec ("{$pfb['script']} dedup x {$pfb['dmax']} {$pfb['dedup']} {$pfb['ccexclude']} {$pfb['ccwhite']} {$pfb['ccblack']} >> {$pfb['log']} 2>&1"); + } + + ################################# + # CONFIGURE ALIASES # + ################################# + + $list_type = array ("pfblockernglistsv4" => "_v4", "pfblockernglistsv6" => "_v6"); + foreach ($list_type as $ip_type => $vtype) { + if ($config['installedpackages'][$ip_type]['config'] != "" && $pfb['enable'] == "on") { + $runonce = 0; + foreach ($config['installedpackages'][$ip_type]['config'] as $list) { + $alias = "pfB_" . preg_replace("/\W/","",$list['aliasname']); + + # Determine Folder Location for Alias (return array $pfbarr) + pfb_determine_list_detail($list['action']); + $pfb['skip'] = $pfbarr['skip']; + $pfb_descr = $pfbarr['descr']; + $pfbfolder = $pfbarr['folder']; + + // Re-Save Only Aliases that have been updated only. + // When 'Reputation' is used, all Aliases need to be Updated. + $final_alias = array(); + if ($pfb['dedup'] == "on" || $pfb['pdup'] == "on") { + if (!empty($pfb_alias_lists_all)) + $final_alias = array_unique($pfb_alias_lists_all); + } else { + if (!empty($pfb_alias_lists)) + $final_alias = array_unique($pfb_alias_lists); + } + + if ($list['action'] != "Disabled") { + #remove empty lists files if any + if (is_array($list['row'])) { + $update = 0; + ${$alias} = ""; + foreach ($list['row'] as $row) { + if ($row['url'] != "" && $row['state'] != "Disabled") { + if ($vtype == "_v4") { + $header_url = "{$row['header']}"; + } else { + $header_url = "{$row['header']}_v6"; + } + $pfctlck = exec ("/sbin/pfctl -vvsTables | grep -A1 {$alias} | awk '/Addresses/ {s+=$2}; END {print s}'"); + + # Update Alias if List File Exists and its been updated or if the Alias URL Table is Empty. + if (file_exists($pfbfolder . "/" . $header_url . ".txt") && in_array($alias, $final_alias) || file_exists($pfbfolder . "/" . $header_url . ".txt") && empty($pfctlck)) { + # Script to run Suppression process (Print Header Only) + if ($pfb['supp'] == "on" && $vtype == "_v4" && $runonce == 0 && $pfb['supp_update']) { + exec ("{$pfb['script']} suppress x x x suppressheader >> {$pfb['log']} 2>&1"); + $runonce++; + } + # Script to run Suppression Process (Body) + if ($pfb['supp'] == "on" && $vtype == "_v4" && $pfb['supp_update']) { + if ($pfb['dup'] == "on" || !$pfb['skip']) { + # Execute if Duplication Process is Enabled or List is Permit or Match + exec ("{$pfb['script']} suppress x x x {$header_url}\|{$pfbfolder}/ >> {$pfb['log']} 2>&1"); + } else { + # Execute if Duplication Process is Disabled + exec ("{$pfb['script']} suppress x x off {$header_url}\|{$pfbfolder}/ >> {$pfb['log']} 2>&1"); + } + } + ${$alias} .= file_get_contents($pfbfolder . '/' . $header_url . '.txt'); + $update++; + } + } + } + } + + #check custom network list + if ($vtype == "_v4") { + $aliasname = "{$list['aliasname']}_custom"; + } else { + $aliasname = "{$list['aliasname']}_custom_v6"; + } + + # Update Alias if List File Exists and its been updated or if the Alias URL Table is Empty. + $pfctlck = exec ("/sbin/pfctl -vvsTables | grep -A1 {$alias} | awk '/Addresses/ {s+=$2}; END {print s}'"); + + if (pfbng_text_area_decode($list['custom']) != "") { + if (file_exists($pfbfolder . "/" . $aliasname . ".txt") && in_array($alias, $final_alias) || file_exists($pfbfolder . "/" . $aliasname . ".txt") && empty($pfctlck)) { + ${$alias} .= file_get_contents($pfbfolder . '/' . $aliasname . '.txt'); + $update++; + } + } + # Determine Validity of Alias URL Tables/Rules. ie: Don't create Empty URL Tables or Aliases + if (${$alias} == "" && empty($pfctlck)) { + unlink_if_exists($pfb['aliasdir'] . '/' . $alias. '.txt'); + } else { + // Save Only Aliases that have been updated. + if ($update > 0) { + @file_put_contents($pfb['aliasdir'] . '/' . $alias. '.txt',${$alias}, LOCK_EX); + } + + $alias_log = $list['aliaslog']; + #create alias + $new_aliases_list[] = "{$alias}"; + + $new_aliases[] = array( "name" => "{$alias}", + "url" => "{$pfb['weblocal']}?pfb={$alias}", + "updatefreq" => "32", + "address" => "", + "descr" => "pfBlockerNG {$pfb_descr} List Alias", + "type" => "urltable", + "detail" => "DO NOT EDIT THIS ALIAS" + ); + + #Create rule if action permits + switch ($list['action']) { + case "Deny_Both": + case "Deny_Outbound": + $rule = $base_rule; + $rule['type'] = "{$pfb['deny_action_outbound']}"; + if ($vtype == "_v6") + $rule['ipprotocol'] = "inet6"; + if ($pfb['float'] == "on") + $rule['direction'] = "any"; + $rule['descr'] = "{$alias}{$pfb['suffix']}"; + $rule['source'] = array ("any" => ""); + $rule['destination'] = array ("address" => "{$alias}"); + if ($pfb['config']['enable_log'] == "on" || $alias_log == "enabled") + $rule['log'] = ""; + $deny_outbound[] = $rule; + if ($list['action'] != "Deny_Both") + break; + case "Deny_Inbound": + $rule = $base_rule; + $rule['type'] = "{$pfb['deny_action_inbound']}"; + if ($vtype == "_v6") + $rule['ipprotocol'] = "inet6"; + if ($pfb['float'] == "on") + $rule['direction'] = "any"; + $rule['descr'] = "{$alias}{$pfb['suffix']}"; + $rule['source'] = array("address" => "{$alias}"); + $rule['destination'] = array ("any" => ""); + if ($pfb['config']['enable_log'] == "on" || $alias_log == "enabled") + $rule['log'] = ""; + $deny_inbound[] = $rule; + break; + case "Permit_Both": + case "Permit_Outbound": + $rule = $base_rule; + $rule['type'] = "pass"; + if ($vtype == "_v6") + $rule['ipprotocol'] = "inet6"; + if ($pfb['float'] == "on") + $rule['direction'] = "any"; + $rule['descr'] = "{$alias}{$pfb['suffix']}"; + $rule['source'] = array ("any" => ""); + $rule['destination'] = array ("address" => "{$alias}"); + if ($pfb['config']['enable_log'] == "on" || $alias_log == "enabled") + $rule['log'] = ""; + $permit_outbound[] = $rule; + if ($list['action'] != "Permit_Both") + break; + case "Permit_Inbound": + $rule = $base_rule; + $rule['type'] = "pass"; + if ($vtype == "_v6") + $rule['ipprotocol'] = "inet6"; + if ($pfb['float'] == "on") + $rule['direction'] = "any"; + $rule['descr'] = "{$alias}{$pfb['suffix']}"; + $rule['source'] = array ("address" => "{$alias}"); + $rule['destination'] = array ("any" => ""); + if ($pfb['config']['enable_log'] == "on" || $alias_log == "enabled") + $rule['log'] = ""; + $permit_inbound[] = $rule; + break; + case "Match_Both": + case "Match_Outbound": + $rule = $base_rule_float; + $rule['type'] = "match"; + if ($vtype == "_v6") + $rule['ipprotocol'] = "inet6"; + $rule['direction'] = "any"; + $rule['descr'] = "{$alias}{$pfb['suffix']}"; + $rule['source'] = array ("any" => ""); + $rule['destination'] = array ("address" => "{$alias}"); + if ($pfb['config']['enable_log'] == "on" || $alias_log == "enabled") + $rule['log'] = ""; + $match_outbound[] = $rule; + if ($list['action'] != "Match_Both") + break; + case "Match_Inbound": + $rule = $base_rule_float; + $rule['type'] = "match"; + if ($vtype == "_v6") + $rule['ipprotocol'] = "inet6"; + $rule['direction'] = "any"; + $rule['descr'] = "{$alias}{$pfb['suffix']}"; + $rule['source'] = array ("address" => "{$alias}"); + $rule['destination'] = array ("any" => ""); + if ($pfb['config']['enable_log'] == "on" || $alias_log == "enabled") + $rule['log'] = ""; + $match_inbound[] = $rule; + break; + } + } + #mark pfctl aliastable for cleanup + if (!in_array($alias, $aliases_list)) { + $aliases_list[] = "{$alias}"; + } + } else { + #unlink previous pfblockerNG alias list if any + unlink_if_exists($pfb['aliasdir'] . '/' . $alias . '.txt'); + } + } + } + } + # Clear Variables + ${$alias} = ""; + + + ######################################### + # UPDATE pfSense ALIAS TABLES # + ######################################### + + #update pfsense alias table + if (is_array($config['aliases']['alias'])) { + foreach ($config['aliases']['alias'] as $cbalias) { + if (preg_match("/pfB_/",$cbalias['name'])) { + #mark pfctl aliastable for cleaning + if (!in_array($cbalias['name'], $aliases_list)) { + $aliases_list[] = $cbalias['name']; #mark aliastable for cleaning + } + #remove previous aliastable file if alias is not defined any more + if (!in_array($cbalias['name'], $new_aliases_list)) { + unlink_if_exists($pfb['aliasdir'] . '/' . $cbalias['name'] . ".txt"); + } + } else { + $new_aliases[] = $cbalias; + + # Check Table Size + if (file_exists($pfb['aliasdir'] . '/' . $alias . '.txt') && $message == "") { + preg_match("/(\d+)/",exec("/usr/bin/grep -c ^ " . $pfb['aliasdir'] . '/' . $alias . '.txt'),$matches); + } + if (($matches[1] * 2.1) >= $pfb['table_limit']) { + #alias table too large + $message = "{$alias} alias table is too large. Reduce networks in list or increase 'Firewall Maximum Table Entries' value to at least " . (int)($matches[1] * 2.1) . ' in "system - advanced - Firewall/NAT" . '; + } + } + } + } + + #apply new alias table to xml + if ($message == "") { + $config['aliases']['alias'] = $new_aliases; + } + # UNSET Variables + unset($new_aliases, $cbalias); + + + ######################### + # Assign Rules # + ######################### + + # Only Execute if AutoRules are defined or if an Alias has been removed. + if ($pfb['autorules'] || $pfb['enable'] == "" || $pfb['remove']) { + if (count($deny_inbound) > 0 || count($permit_inbound) > 0 || count($match_inbound) > 0) { + if ($pfb['inbound_interfaces'] == "") { + $message = "Unable to apply rules. Inbound Interface option not configured."; + } + } + if (count($deny_outbound) > 0 || count($permit_outbound) > 0 || count($match_outbound) > 0) { + if ($pfb['outbound_interfaces'] == "") { + $message = "Unable to apply rules. Outbound Interface option not configured."; + } + } + + if ($message == "") { + $new_rules = array(); + $permit_rules = array(); + $match_rules = array(); + $other_rules = array(); + $fpermit_rules = array(); + $fmatch_rules = array(); + $fother_rules = array(); + + # Collect All Existing Rules + $rules = $config['filter']['rule']; + # Collect Existing pfSense Rules 'Pass', 'Match' and 'Other' pfSense rules into new Arrays. + if (!empty($rules)) { + foreach ($rules as $rule) { + if (!preg_match("/pfB_.*" . $pfb['suffix'] . "/",$rule['descr'])) { + // Floating rules collection 'Floating Pass/Match'. Balance to 'other' + if ($pfb['float'] == "on") { + if ($rule['type'] == "pass" && $rule['floating'] == "yes") { + $fpermit_rules[] = $rule; + } elseif ($rule['type'] == "match" && $rule['floating'] == "yes") { + $fmatch_rules[] = $rule; + } elseif ($rule['floating'] == "yes") { + $fother_rules[] = $rule; + } else { + $other_rules[] = $rule; + } + } else { + // Collect only 'Selected Inbound and Outbound Interfaces'. Balance to 'Other' + if (in_array($rule['interface'],$pfb['inbound_interfaces']) || in_array($rule['interface'],$pfb['outbound_interfaces'])) { + // Floating Rules 'off'. Collect 'Floating Other', Balance to 'Other' + if ($rule['floating'] == "yes") { + $fother_rules[] = $rule; + } elseif ($rule['type'] == "pass") { + if ($pfb['order'] == "order_0") { + $other_rules[] = $rule; + } else { + $permit_rules[] = $rule; + } + } elseif ($rule['type'] == "match") { + if ($pfb['order'] == "order_0") { + $other_rules[] = $rule; + } else { + $match_rules[] = $rule; + } + } else { + $other_rules[] = $rule; + } + } else { + if ($rule['floating'] == "yes") { + $fother_rules[] = $rule; + } else { + $other_rules[] = $rule; + } + } + } + } + } + } + + ################################################################################# + # PASS/MATCH RULES ORDER(p/m) # + # ORDER 0 - pfBlockerNG / All other Rules # + # ORDER 1 - pfSense (p/m) / pfBlockerNG (p/m) / pfBlockerNG Block/Reject # + # ORDER 2 - pfBlockerNG (p/m) / pfSense (p/m) / pfBlockerNG Block/Reject # + # ORDER 3 - pfBlockerNG (p/m) / pfBlockerNG Block/Reject / pfSense (p/m) # + ################################################################################# + + if ($pfb['float'] == "") { + if (!empty($fother_rules)) { + foreach ($fother_rules as $cb_rules) { + $new_rules[] = $cb_rules; + } + } + } + if (!empty($fpermit_rules) && $pfb['order'] == "order_1") { + foreach ($fpermit_rules as $cb_rules) { + $new_rules[] = $cb_rules; + } + } + if (!empty($fmatch_rules) && $pfb['order'] == "order_1") { + foreach ($fmatch_rules as $cb_rules) { + $new_rules[] = $cb_rules; + } + } + + # Define Inbound Interface Rules + if (!empty($pfb['inbound_interfaces'])) { + $counter = 0; + foreach ($pfb['inbound_interfaces'] as $inbound_interface) { + if (!empty($permit_rules) && $pfb['order'] == "order_1") { + foreach ($permit_rules as $cb_rules) { + if ($cb_rules['interface'] == $inbound_interface) + $new_rules[] = $cb_rules; + } + } + if (!empty($match_rules) && $pfb['order'] == "order_1") { + foreach ($match_rules as $cb_rules) { + if ($cb_rules['interface'] == $inbound_interface) + $new_rules[] = $cb_rules; + } + } + # Match Inbound Rules defined as Floating Only. + if (!empty($match_inbound) && $counter == 0) { + foreach ($match_inbound as $cb_rules) { + $cb_rules['interface'] = $pfb['inbound_floating']; + $new_rules[] = $cb_rules; + $counter ++; + } + } + if (!empty($permit_inbound)) { + foreach ($permit_inbound as $cb_rules) { + $cb_rules['interface'] = $inbound_interface; + $new_rules[] = $cb_rules; + } + } + if (!empty($fpermit_rules) && $pfb['order'] == "order_2") { + foreach ($fpermit_rules as $cb_rules) { + $new_rules[] = $cb_rules; + } + } + if (!empty($fmatch_rules) && $pfb['order'] == "order_2") { + foreach ($fmatch_rules as $cb_rules) { + $new_rules[] = $cb_rules; + } + } + if (!empty($permit_rules) && $pfb['order'] == "order_2") { + foreach ($permit_rules as $cb_rules) { + if ($cb_rules['interface'] == $inbound_interface) + $new_rules[] = $cb_rules; + } + } + if (!empty($match_rules) && $pfb['order'] == "order_2") { + foreach ($match_rules as $cb_rules) { + if ($cb_rules['interface'] == $inbound_interface) + $new_rules[] = $cb_rules; + } + } + if (!empty($deny_inbound)) { + foreach ($deny_inbound as $cb_rules) { + $cb_rules['interface'] = $inbound_interface; + $new_rules[] = $cb_rules; + } + } + } + } + + # Define Outbound Interface Rules + if (!empty($pfb['outbound_interfaces'])) { + $counter = 0; + foreach ($pfb['outbound_interfaces'] as $outbound_interface) { + if (!empty($permit_rules) && $pfb['order'] == "order_1") { + foreach ($permit_rules as $cb_rules) { + if ($cb_rules['interface'] == $outbound_interface) + $new_rules[] = $cb_rules; + } + } + if (!empty($match_rules) && $pfb['order'] == "order_1") { + foreach ($match_rules as $cb_rules) { + if ($cb_rules['interface'] == $outbound_interface) + $new_rules[] = $cb_rules; + } + } + # Match Outbound Rules defined as Floating Only. + if (!empty($match_outbound) && $counter == 0) { + foreach ($match_outbound as $cb_rules) { + $cb_rules['interface'] = $pfb['outbound_floating']; + $new_rules[] = $cb_rules; + $counter++; + } + } + if (!empty($permit_outbound)) { + foreach ($permit_outbound as $cb_rules) { + $cb_rules['interface'] = $outbound_interface; + $new_rules[] = $cb_rules; + } + } + if (!empty($permit_rules) && $pfb['order'] == "order_2") { + foreach ($permit_rules as $cb_rules) { + if ($cb_rules['interface'] == $outbound_interface) + $new_rules[] = $cb_rules; + } + } + if (!empty($match_rules) && $pfb['order'] == "order_2") { + foreach ($match_rules as $cb_rules) { + if ($cb_rules['interface'] == $outbound_interface) + $new_rules[] = $cb_rules; + } + } + if (!empty($deny_outbound)) { + foreach ($deny_outbound as $cb_rules) { + $cb_rules['interface'] = $outbound_interface; + $new_rules[] = $cb_rules; + } + } + } + } + + if (!empty($fpermit_rules) && $pfb['order'] == "order_0") { + foreach ($fpermit_rules as $cb_rules) { + $new_rules[] = $cb_rules; + } + } + if (!empty($fmatch_rules) && $pfb['order'] == "order_0") { + foreach ($fmatch_rules as $cb_rules) { + $new_rules[] = $cb_rules; + } + } + if (!empty($fpermit_rules) && $pfb['order'] == "order_3") { + foreach ($fpermit_rules as $cb_rules) { + $new_rules[] = $cb_rules; + } + } + if (!empty($fmatch_rules) && $pfb['order'] == "order_3") { + foreach ($fmatch_rules as $cb_rules) { + $new_rules[] = $cb_rules; + } + } + if (!empty($permit_rules) && $pfb['order'] == "order_3") { + foreach ($permit_rules as $cb_rules) { + $new_rules[] = $cb_rules; + } + } + if (!empty($match_rules) && $pfb['order'] == "order_3") { + foreach ($match_rules as $cb_rules) { + $new_rules[] = $cb_rules; + } + } + if ($pfb['float'] == "on") { + if (!empty($fother_rules)) { + foreach ($fother_rules as $cb_rules) { + $new_rules[] = $cb_rules; + } + } + } + if (!empty($other_rules)) { + foreach ($other_rules as $cb_rules) { + $new_rules[] = $cb_rules; + } + } + + # Save New Rule Order to Config + $config['filter']['rule'] = $new_rules; + } + $log = "\n {$message} \n"; + pfb_logger("{$log}","1"); + + # UNSET arrays + unset ($cb_rules,$permit_inbound,$permit_outbound,$deny_inbound,$deny_outbound,$match_inbound,$match_outbound); + unset ($other_rules,$fother_rules,$permit_rules,$fpermit_rules,$match_rules,$fmatch_rules); + } + + + ################################# + # Closing Processes # + ################################# + + #uncheck Reusing Existing Downloads Check box + if (!$pfb['save'] && $pfb['enable'] == "on") + $config['installedpackages']['pfblockerng']['config'][0]['pfb_reuse'] = ""; + + # Save all Changes to pfSense config file + write_config(); + + # If 'Rule Changes' are found, utilize the 'filter_configure()' function, if not, utilize 'pfctl replace' command + if ($pfb['autorules'] && $rules != $new_rules || $pfb['enable'] == "" || $pfb['remove']) { + require_once("filter.inc"); + + if (!$pfb['save']) { + $log = "\n===[ Aliastables / Rules ]================================\n\n"; + pfb_logger("{$log}","1"); + + $log = "Firewall Rule Changes Found, Applying Filter Reload \n"; + pfb_logger("{$log}","1"); + } + + # Remove all pfBlockerNG Alias tables + if (!empty($aliases_list)) { + foreach ($aliases_list as $table) { + exec ("/sbin/pfctl -t " . escapeshellarg($table) . " -T kill 2>&1", $pfb_null); + } + } + + #load filter file which will create the pfctl tables + filter_configure(); + + // Call function for NanoBSD/Ramdisk processes. + pfb_aliastables("update"); + } else { + # Don't Execute on User 'Save' + if (!$pfb['save']) { + + $log = "\n===[ Aliastables / Rules ]================================\n\n"; + pfb_logger("{$log}","1"); + + $log = "No Changes to Firewall Rules, Skipping Filter Reload \n"; + pfb_logger("{$log}","1"); + + // Re-Save Only Aliases that have been updated only. + // When 'Reputation' is used, all Aliases Need to be Updated. + $final_alias = array(); + if ($pfb['dedup'] == "on" || $pfb['pdup'] == "on") { + if (!empty($pfb_alias_lists_all)) + $final_alias = array_unique($pfb_alias_lists_all); + } else { + if (!empty($pfb_alias_lists)) + $final_alias = array_unique($pfb_alias_lists); + } + + if (!empty($final_alias)) { + foreach ($final_alias as $final) { + $log = "\n Updating: {$final} \n"; + pfb_logger("{$log}","1"); + $result_pfctl = ""; + exec ("/sbin/pfctl -t " . escapeshellarg($final) . " -T replace -f " . $pfb['aliasdir'] . "/" . escapeshellarg($final) . ".txt 2>&1", $result_pfctl); + $log = implode($result_pfctl); + pfb_logger("{$log}","1"); + } + + // Call function for NanoBSD/Ramdisk processes. + pfb_aliastables("update"); + } else { + $log = "\nNo Changes to Aliases, Skipping pfctl Update \n"; + pfb_logger("{$log}","1"); + } + } + } + # UNSET Variables + unset($rules, $new_rules); + + #sync config + pfblockerng_sync_on_changes(); + + ################################# + # FINAL REPORTING # + ################################# + + # Only run with CRON or Force Invoked Process + if ((!$pfb['save'] && $pfb['dupcheck'] && $pfb['enable'] == "on") || $pfb['summary']) { + # Script to run Final Script Processes. + exec ("{$pfb['script']} closing {$pfb['dup']} >> {$pfb['log']} 2>&1"); + } + + if ($pfb['enable'] == "on" && !$pfb['save']) { + $log = "\n\n UPDATE PROCESS ENDED [ NOW ]\n"; + pfb_logger("{$log}","1"); + } + + + ######################################### + # Define/Apply CRON Jobs # + ######################################### + + # Clear any existing pfBlockerNG Cron Jobs + install_cron_job("pfblockerng.php cron", false); + + # Replace Cron job with any User Changes to $pfb_min + if ($pfb['enable'] == "on") { + # Define pfBlockerNG CRON Job + $pfb_cmd = "/usr/local/bin/php /usr/local/www/pfblockerng/pfblockerng.php cron >> {$pfb['log']} 2>&1"; + # $pfb['min'] ( User Defined Variable. Variable defined at start of Script ) + $pfb_hour = "*"; + $pfb_mday = "*"; + $pfb_month = "*"; + $pfb_wday = "*"; + $pfb_who = "root"; + + install_cron_job($pfb_cmd, true, $pfb['min'], $pfb_hour, $pfb_mday, $pfb_month, $pfb_wday, $pfb_who); + } + + # Clear any existing pfBlockerNG MaxMind CRON Job + install_cron_job("pfblockerng.php dc", false); + + if ($pfb['enable'] == "on") { + # Define pfBlockerNG MaxMind CRON Job + $pfb_gcmd = "/usr/local/bin/php /usr/local/www/pfblockerng/pfblockerng.php dc >> {$pfb['geolog']} 2>&1"; + + # MaxMind GeoIP Cron Hour is randomized between 0-23 Hour to minimize effect on MaxMind Website + + $pfb_gmin = "0"; + $pfb_ghour = rand(0,23); + $pfb_gmday = "1,2,3,4,5,6,7"; + $pfb_gmonth = "*"; + $pfb_gwday = "2"; + $pfb_gwho = "root"; + + install_cron_job($pfb_gcmd, true, $pfb_gmin, $pfb_ghour, $pfb_gmday, $pfb_gmonth, $pfb_gwday, $pfb_gwho); + } +} + + +function pfblockerng_validate_input($post, &$input_errors) { + global $config; + foreach ($post as $key => $value) { + if (empty($value)) + continue; + if ($key == "message_size_limit" && !is_numeric($value)) + $input_errors[] = "Message size limit must be numeric."; + if ($key == "process_limit" && !is_numeric($value)) + $input_errors[] = "Process limit must be numeric."; + if ($key == "freq" && (!preg_match("/^\d+(h|m|d)$/",$value) || $value == 0)) + $input_errors[] = "A valid number with a time reference is required for the field 'Frequency'"; + if (substr($key, 0, 2) == "dc" && !is_hostname($value)) + $input_errors[] = "{$value} is not a valid host name."; + if (substr($key, 0, 6) == "domain" && is_numeric(substr($key, 6))) { + if (!is_domain($value)) + $input_errors[] = "{$value} is not a valid domain name."; + } else if (substr($key, 0, 12) == "mailserverip" && is_numeric(substr($key, 12))) { + if (empty($post['domain' . substr($key, 12)])) + $input_errors[] = "Domain for {$value} cannot be blank."; + if (!is_ipaddr($value) && !is_hostname($value)) + $input_errors[] = "{$value} is not a valid IP address or host name."; + } + } +} + + +function pfblockerng_php_install_command() { + require_once("/usr/local/www/pfblockerng/pfblockerng.php"); + global $config,$pfb; + pfb_global(); + + // Remove previously used CC folder location if exists + @rmdir_recursive("{$pfb['dbdir']}/cc"); + + # Uncompress Country Code File and delete Archive after extraction. + @rename("{$pfb['dbdir']}/countrycodes.tar.bz2", "{$pfb['ccdir']}/countrycodes.tar.bz2"); + exec("cd {$pfb['ccdir']}; /usr/bin/tar -jxvf {$pfb['ccdir']}/countrycodes.tar.bz2"); + unlink_if_exists("{$pfb['ccdir']}/countrycodes.tar.bz2"); + # Download MaxMind Files and Create Country Code files and Build Continent XML Files + update_output_window(gettext("Downloading MaxMind Country Databases. This may take a minute...")); + exec("/bin/sh /usr/local/pkg/pfblockerng/geoipupdate.sh all >> {$pfb['geolog']} 2>&1"); + + update_output_window(gettext("MaxMind Country Database downloads completed...")); + update_output_window(gettext("Converting MaxMind Country Databases for pfBlockerNG. This may take a few minutes...")); + pfblockerng_uc_countries(); + update_output_window(gettext("Creating pfBlockerNG Continenet XML Files...")); + pfblockerng_get_countries(); + update_output_window(gettext("Completed Creating pfBlockerNG Continenet XML Files...")); + + // Remove Original Maxmind Database Files + @unlink_if_exists("{$pfb['dbdir']}/GeoIPCountryCSV.zip"); + @unlink_if_exists("{$pfb['dbdir']}/GeoIPCountryWhois.csv"); + @unlink_if_exists("{$pfb['dbdir']}/GeoIPv6.csv"); + @unlink_if_exists("{$pfb['dbdir']}/country_continent.csv"); + + # Add Widget to Dashboard + update_output_window(gettext("Adding pfBlockerNG Widget to Dashboard.")); + if ($pfb['keep'] == "on" && !empty($pfb['widgets'])) { + // Restore previous Widget setting if "Keep" is enabled. + $config['widgets']['sequence'] = $pfb['widgets']; + } else { + $widgets = $config['widgets']['sequence']; + if (!preg_match("/pfblockerng-container/", $widgets)) { + if (empty($widgets)) { + $config['widgets']['sequence'] = "pfblockerng-container:col2:show"; + } else { + $config['widgets']['sequence'] .= ",pfblockerng-container:col2:show"; + } + } + } +} + + +function pfblockerng_php_deinstall_command() { + require_once("config.inc"); + global $config,$pfb; + + # Set these two variables to Disable pfBlockerNG on De-Install + $pfb['save'] = TRUE; + $pfb['install'] = TRUE; + sync_package_pfblockerng(); + rmdir_recursive("/usr/local/pkg/pfblockerng"); + rmdir_recursive("/usr/local/www/pfblockerng"); + + # Maintain pfBlockerNG Settings and Database Files if $pfb['keep'] is ON. + if ($pfb['keep'] != "on") { + # Remove pfBlockerNG Log and DB Folder + rmdir_recursive("{$pfb['dbdir']}"); + rmdir_recursive("{$pfb['logdir']}"); + + // Remove Aliastables archive and earlyshellcmd if found. + @unlink_if_exists("{$pfb['aliasarchive']}"); + if (is_array($config['system']['earlyshellcmd'])) { + $a_earlyshellcmd = &$config['system']['earlyshellcmd']; + if (preg_grep("/pfblockerng.sh aliastables/", $a_earlyshellcmd)) { + $a_earlyshellcmd = preg_grep("/pfblockerng.sh aliastables/", $a_earlyshellcmd, PREG_GREP_INVERT); + } + } + + # Remove Settings from Config + if (is_array($config['installedpackages']['pfblockerng'])) + unset($config['installedpackages']['pfblockerng']); + if (is_array($config['installedpackages']['pfblockerngglobal'])) + unset($config['installedpackages']['pfblockerngglobal']); + if (is_array($config['installedpackages']['pfblockerngsync'])) + unset($config['installedpackages']['pfblockerngsync']); + if (is_array($config['installedpackages']['pfblockerngreputation'])) + unset($config['installedpackages']['pfblockerngreputation']); + if (is_array($config['installedpackages']['pfblockernglistsv4'])) + unset($config['installedpackages']['pfblockernglistsv4']); + if (is_array($config['installedpackages']['pfblockernglistsv6'])) + unset($config['installedpackages']['pfblockernglistsv6']); + if (is_array($config['installedpackages']['pfblockerngafrica'])) + unset($config['installedpackages']['pfblockerngafrica']); + if (is_array($config['installedpackages']['pfblockerngantartica'])) + unset($config['installedpackages']['pfblockerngantartica']); + if (is_array($config['installedpackages']['pfblockerngasia'])) + unset($config['installedpackages']['pfblockerngasia']); + if (is_array($config['installedpackages']['pfblockerngeurope'])) + unset($config['installedpackages']['pfblockerngeurope']); + if (is_array($config['installedpackages']['pfblockerngnorthamerica'])) + unset($config['installedpackages']['pfblockerngnorthamerica']); + if (is_array($config['installedpackages']['pfblockerngoceania'])) + unset($config['installedpackages']['pfblockerngoceania']); + if (is_array($config['installedpackages']['pfblockerngsouthamerica'])) + unset($config['installedpackages']['pfblockerngsouthamerica']); + if (is_array($config['installedpackages']['pfblockerngtopspammers'])) + unset($config['installedpackages']['pfblockerngtopspammers']); + if (is_array($config['installedpackages']['pfblockerngproxyandsatellite'])) + unset($config['installedpackages']['pfblockerngproxyandsatellite']); + } + + # Remove Widget (code from Snort deinstall) + $pfb['widgets'] = $config['widgets']['sequence']; + if (!empty($pfb['widgets'])) { + $widgetlist = explode(",", $pfb['widgets']); + foreach ($widgetlist as $key => $widget) { + if (strstr($widget, "pfblockerng-container")) { + unset($widgetlist[$key]); + break; + } + } + $config['widgets']['sequence'] = implode(",", $widgetlist); + } + update_output_window(gettext("pfBlockerNG has been Uninstalled")); +} + +/* Uses XMLRPC to synchronize the changes to a remote node */ +function pfblockerng_sync_on_changes() { + global $config, $g, $pfb_sync; + + // Create Array of Sync Settings and exit if Sync is Disabled. + if (is_array($config['installedpackages']['pfblockerngsync']['config'][0])) { + $pfb_sync = $config['installedpackages']['pfblockerngsync']['config'][0]; + if ($pfb_sync['varsynconchanges'] == "disabled" || $pfb_sync['varsynconchanges'] == "") + return; + + $synctimeout = $pfb_sync['varsynctimeout']; + } else { + return; + } + + log_error("[pfBlockerNG] XMLRPC sync is starting."); + + if (is_array($config['installedpackages']['pfblockerngsync']['config'])) { + switch ($pfb_sync['varsynconchanges']) { + case "manual": + if (is_array($pfb_sync[row])) { + $rs = $pfb_sync[row]; + } else { + log_error("[pfBlockerNG] XMLRPC sync is enabled but there are no replication targets configured."); + return; + } + break; + case "auto": + if (is_array($config['installedpackages']['carpsettings']) && is_array($config['installedpackages']['carpsettings']['config'])){ + $system_carp = $config['installedpackages']['carpsettings']['config'][0]; + $rs[0]['varsyncipaddress'] = $system_carp['synchronizetoip']; + $rs[0]['varsyncusername'] = $system_carp['username']; + $rs[0]['varsyncpassword'] = $system_carp['password']; + + // XMLRPC sync is currently only supported over connections using the same protocol and port as this system + if ($config['system']['webgui']['protocol'] == "http") { + $rs[0]['varsyncprotocol'] = "http"; + } else { + $rs[0]['varsyncprotocol'] = "https"; + } + + if ($system_carp['synchronizetoip'] == "") { + log_error("[pfBlockerNG] XMLRPC sync is enabled but there are no replication targets configured."); + return; + } + } else { + log_error("[pfBlockerNG] XMLRPC sync is enabled but there are no replication targets configured."); + return; + } + break; + default: + return; + break; + } + if (is_array($rs)) { + foreach ($rs as $sh) { + // Only Sync Enabled Replication Targets + if ($sh['varsyncdestinenable'] == "ON") { + $sync_to_ip = $sh['varsyncipaddress']; + $port = $sh['varsyncport']; + $password = htmlspecialchars($sh['varsyncpassword']); + $protocol = $sh['varsyncprotocol']; + + if (!empty($sh['varsyncusername'])) { + $username = $sh['varsyncusername']; + } else { + $username = "admin"; + } + + pfblockerng_do_xmlrpc_sync($sync_to_ip, $port, $protocol, $username, $password, $synctimeout); + } + } + if ($success) + log_error("[pfBlockerNG] XMLRPC sync completed successfully."); + } + } +} + + +/* Do the actual XMLRPC sync */ +function pfblockerng_do_xmlrpc_sync($sync_to_ip, $port, $protocol, $username, $password, $synctimeout) { + global $config, $g, $pfb_sync; + $success = TRUE; + + /* Exit on missing parameters */ + if (empty($sync_to_ip) || empty($password)) { + log_error("[pfBlockerNG] XMLRPC sync parameter missing (host IP or password) ... aborting xmlrpc sync"); + $success = FALSE; + return $success; + } + + /* Do not attempt a package sync while booting up or installing package */ + if ($g['booting'] || $g['pfblockerng_postinstall']) { + log_error("[pfBlockerNG] XMLRPC sync to Replication targets terminated during boot up or during package reinstallation."); + $success = FALSE; + return $success; + } + + // Validate Replication Target IP Address and Port Settings + if (!is_ipaddr($sync_to_ip) || !is_port($port)) { + log_error("[pfBlockerNG] XMLRPC sync terminated due to mis-configured Replication Target IP Address or Port settings."); + $success = FALSE; + return $success; + } + + /* Test key variables and set defaults if empty */ + if (empty($synctimeout)) + $synctimeout = 150; + + $url = "{$protocol}://{$sync_to_ip}"; + + if ($port == "") { $port = $config['system']['webgui']['port']; }; + /* If port is empty lets rely on the protocol selection */ + if ($port == "") { + if ($config['system']['webgui']['protocol'] == "http") { + $port = "80"; + } else { + $port = "443"; + } + } + /* xml will hold the sections to sync */ + $xml = array(); + // If User Disabled, remove 'General Tab Customizations' from Sync + if ($config['installedpackages']['pfblockerngsync']['config'][0]['syncinterfaces'] == "") + $xml['pfblockerng'] = $config['installedpackages']['pfblockerng']; + $xml['pfblockerngreputation'] = $config['installedpackages']['pfblockerngreputation']; + $xml['pfblockernglistsv4'] = $config['installedpackages']['pfblockernglistsv4']; + $xml['pfblockernglistsv6'] = $config['installedpackages']['pfblockernglistsv6']; + $xml['pfblockerngtopspammers'] = $config['installedpackages']['pfblockerngtopspammers']; + $xml['pfblockerngafrica'] = $config['installedpackages']['pfblockerngafrica']; + $xml['pfblockerngantartica'] = $config['installedpackages']['pfblockerngantartica']; + $xml['pfblockerngasia'] = $config['installedpackages']['pfblockerngasia']; + $xml['pfblockerngeurope'] = $config['installedpackages']['pfblockerngeurope']; + $xml['pfblockerngnorthamerica'] = $config['installedpackages']['pfblockerngnorthamerica']; + $xml['pfblockerngoceania'] = $config['installedpackages']['pfblockerngoceania']; + $xml['pfblockerngsouthamerica'] = $config['installedpackages']['pfblockerngsouthamerica']; + $xml['pfblockerngproxyandsatellite'] = $config['installedpackages']['pfblockerngproxyandsatellite']; + + /* assemble xmlrpc payload */ + $params = array( + XML_RPC_encode($password), + XML_RPC_encode($xml) + ); + + /* set a few variables needed for sync code borrowed from filter.inc */ + log_error("[pfBlockerNG] XMLRPC syncing to {$url}:{$port}."); + $method = 'pfsense.merge_installedpackages_section_xmlrpc'; + $msg = new XML_RPC_Message($method, $params); + $cli = new XML_RPC_Client('/xmlrpc.php', $url, $port); + $cli->setCredentials($username, $password); + if ($g['debug']) { + $cli->setDebug(1); + } + + /* send our XMLRPC message and timeout after defined sync timeout value */ + $resp = $cli->send($msg, $synctimeout); + $error = ""; + if (!$resp) { + log_error("[pfBlockerNG] XMLRPC communications error occurred while attempting sync with {$url}:{$port}."); + file_notice("sync_settings", $error, "pfBlockerNG Settings Sync", ""); + $success = FALSE; + return $success; + } elseif ($resp->faultCode()) { + $cli->setDebug(1); + $resp = $cli->send($msg, $synctimeout); + log_error("[pfBlockerNG] XMLRPC Error received while attempting sync with {$url}:{$port} - Code " . $resp->faultCode() . ": " . $resp->faultString()); + file_notice("sync_settings", $error, "pfBlockerNG Settings Sync", ""); + $success = FALSE; + return $success; + } else { + log_error("[pfBlockerNG] XMLRPC sync successfully completed with {$url}:{$port}."); + } + return $success; +} +?> \ No newline at end of file -- cgit v1.2.3