From 02171133ae4572fed39110042155a0f02038238d Mon Sep 17 00:00:00 2001 From: lilinzhe Date: Thu, 16 Jan 2020 21:12:46 +0800 Subject: [PATCH 1/5] New API: network_address_aliases_get / filter_rules_get network_address_aliases_get --- - Returns address aliaes used by rules. - HTTP: **GET** - Params: none *Example Request* ```bash curl \ -X GET \ --silent \ --insecure \ --header "fauxapi-auth: " \ "https:///fauxapi/v1/?action=network_address_aliases_get" ``` *Example Response* ```javascript { "callid": "5e205fc052956", "action": "network_address_aliases_get", "message": "ok", "data": { "aliases": { "alias": [ { "name": "EasyRuleBlockHostsWAN", "type": "network", "address": "1.2.3.4/32 5.6.7.8/32", "descr": "Hosts blocked from Firewall Log view", "detail": "Entry added Fri, 27 Dec 2019 00:53:01 -0800||\u5df2\u6dfb\u52a0\u6761\u76ee Thu, 16 Jan 2020 03:42:37 -0800" } ] } } } ``` filter_rules_get --- - Returns address aliaes used by rules. - HTTP: **GET** - Params: none *Example Request* ```bash curl \ -X GET \ --silent \ --insecure \ --header "fauxapi-auth: " \ "https:///fauxapi/v1/?action=filter_rules_get" ``` *Example Response* ```javascript { "callid": "5e2060797a602", "action": "filter_rules_get", "message": "ok", "data": { "filter": { "rules": [ { "id": "", "tracker": "1579178400", "type": "pass", "interface": "wan", "ipprotocol": "inet", "tag": "", "tagged": "", "max": "", "max-src-nodes": "", "max-src-conn": "", "max-src-states": "", "statetimeout": "", "statetype": "keep state", "os": "", "protocol": "tcp", "source": { "address": "1.2.1.1" }, "destination": { "any": "", "port": "1-65535" }, "descr": "", "updated": { "time": "1579178400", "username": "admin@192.168.88.1 (Local Database)" }, "created": { "time": "1579178400", "username": "admin@192.168.88.1 (Local Database)" } }, { "type": "block", "interface": "wan", "ipprotocol": "inet", "source": { "address": "EasyRuleBlockHostsWAN" }, "destination": { "any": "" }, "descr": "Easy Rule: Blocked from Firewall Log View", "created": { "time": "1577436781", "username": "Easy Rule" }, "tracker": "1577436781" }, { "type": "drop", "ipprotocol": "inet", "descr": "Default allow LAN to any rule", "interface": "lan", "source": { "network": "lan" }, "destination": { "ip": "192.10.1.1" } } ] } } } ``` Signed-off-by: lilinzhe --- README.md | 131 ++++++++++++++++++ .../files/etc/inc/fauxapi/fauxapi_actions.inc | 48 +++++++ .../inc/fauxapi/fauxapi_pfsense_interface.inc | 22 +++ 3 files changed, 201 insertions(+) diff --git a/README.md b/README.md index eb8e06c..bc5f3b3 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,8 @@ tasks feasible. - [system_reboot](#user-content-system_reboot) - Reboots the pfSense system. - [system_stats](#user-content-system_stats) - Returns various useful system stats. - [system_info](#user-content-system_info) - Returns various useful system info. + - [network_address_aliases_get](#user-content-network_address_aliases_get) - Returns address aliaes used by rules. + - [filter_rules_get](#user-content-filter_rules_get) - Returns firewall filters. ## Approach @@ -930,6 +932,135 @@ curl \ } ``` --- +### network_address_aliases_get + - Returns address aliaes used by rules. + - HTTP: **GET** + - Params: none + +*Example Request* +```bash +curl \ + -X GET \ + --silent \ + --insecure \ + --header "fauxapi-auth: " \ + "https:///fauxapi/v1/?action=network_address_aliases_get" +``` + +*Example Response* +```javascript +{ + "callid": "5e205fc052956", + "action": "network_address_aliases_get", + "message": "ok", + "data": { + "aliases": { + "alias": [ + { + "name": "EasyRuleBlockHostsWAN", + "type": "network", + "address": "1.2.3.4/32 5.6.7.8/32", + "descr": "Hosts blocked from Firewall Log view", + "detail": "Entry added Fri, 27 Dec 2019 00:53:01 -0800||\u5df2\u6dfb\u52a0\u6761\u76ee Thu, 16 Jan 2020 03:42:37 -0800" + } + ] + } + } +} +``` +--- +### filter_rules_get + - Returns firewall filters. + - HTTP: **GET** + - Params: none + +*Example Request* +```bash +curl \ + -X GET \ + --silent \ + --insecure \ + --header "fauxapi-auth: " \ + "https:///fauxapi/v1/?action=filter_rules_get" +``` + +*Example Response* +```javascript +{ + "callid": "5e2060797a602", + "action": "filter_rules_get", + "message": "ok", + "data": { + "filter": { + "rules": [ + { + "id": "", + "tracker": "1579178400", + "type": "pass", + "interface": "wan", + "ipprotocol": "inet", + "tag": "", + "tagged": "", + "max": "", + "max-src-nodes": "", + "max-src-conn": "", + "max-src-states": "", + "statetimeout": "", + "statetype": "keep state", + "os": "", + "protocol": "tcp", + "source": { + "address": "1.2.1.1" + }, + "destination": { + "any": "", + "port": "1-65535" + }, + "descr": "", + "updated": { + "time": "1579178400", + "username": "admin@192.168.88.1 (Local Database)" + }, + "created": { + "time": "1579178400", + "username": "admin@192.168.88.1 (Local Database)" + } + }, + { + "type": "block", + "interface": "wan", + "ipprotocol": "inet", + "source": { + "address": "EasyRuleBlockHostsWAN" + }, + "destination": { + "any": "" + }, + "descr": "Easy Rule: Blocked from Firewall Log View", + "created": { + "time": "1577436781", + "username": "Easy Rule" + }, + "tracker": "1577436781" + }, + { + "type": "drop", + "ipprotocol": "inet", + "descr": "Default allow LAN to any rule", + "interface": "lan", + "source": { + "network": "lan" + }, + "destination": { + "ip": "192.10.1.1" + } + } + ] + } + } +} +``` +--- ## Versions and Testing The FauxAPI has been developed against pfSense 2.3.2, 2.3.3, 2.3.4, 2.3.5, 2.4.3, 2.4.4 it has diff --git a/pfSense-pkg-FauxAPI/files/etc/inc/fauxapi/fauxapi_actions.inc b/pfSense-pkg-FauxAPI/files/etc/inc/fauxapi/fauxapi_actions.inc index 94dce21..812fb1f 100644 --- a/pfSense-pkg-FauxAPI/files/etc/inc/fauxapi/fauxapi_actions.inc +++ b/pfSense-pkg-FauxAPI/files/etc/inc/fauxapi/fauxapi_actions.inc @@ -424,6 +424,54 @@ class fauxApiActions { ); return TRUE; } + + /** + * filter_rules_get() + * + * @return boolean + */ + public function filter_rules_get() { + fauxApiLogger::debug(__METHOD__); + + $rules = $this->PfsenseInterface->filter_rules_get(); + + if (empty($rules)) { + $this->response->http_code = 500; + $this->response->message = 'unable to get filter rule(s)'; + return FALSE; + } + $this->response->http_code = 200; + $this->response->message = 'ok'; + $this->response->data = array( + 'filter' => array( + 'rules' => $rules + ), + ); + return TRUE; + } + + /** + * address_aliases_get() + * + * @return boolean + */ + public function network_address_aliases_get() { + fauxApiLogger::debug(__METHOD__); + + $alias = $this->PfsenseInterface->network_address_aliases_get(); + + if (empty($alias)) { + $this->response->http_code = 500; + $this->response->message = 'unable to get address aliases'; + return FALSE; + } + $this->response->http_code = 200; + $this->response->message = 'ok'; + $this->response->data = array( + 'aliases' => $alias, + ); + return TRUE; + } /** * alias_update_urltables() diff --git a/pfSense-pkg-FauxAPI/files/etc/inc/fauxapi/fauxapi_pfsense_interface.inc b/pfSense-pkg-FauxAPI/files/etc/inc/fauxapi/fauxapi_pfsense_interface.inc index 718e380..abe15af 100644 --- a/pfSense-pkg-FauxAPI/files/etc/inc/fauxapi/fauxapi_pfsense_interface.inc +++ b/pfSense-pkg-FauxAPI/files/etc/inc/fauxapi/fauxapi_pfsense_interface.inc @@ -765,6 +765,28 @@ class fauxApiPfsenseInterface { return \pfSense_get_interface_stats($interface); } + /** + * filter_rules_get() + * + * @return array + */ + public function filter_rules_get(){ + global $config; + fauxApiLogger::debug(__METHOD__); + return $config["filter"]["rule"]; + } + + /** + * network_address_aliases_get() + * + * @return array + */ + public function network_address_aliases_get(){ + global $config; + fauxApiLogger::debug(__METHOD__); + return $config["aliases"]; + } + /** * function_call() * From 140aec0822cb69bc78bfc9e6cbed38a24ea97193 Mon Sep 17 00:00:00 2001 From: lilinzhe Date: Sat, 18 Jan 2020 07:16:20 +0800 Subject: [PATCH 2/5] Alias Management: create - network_address_aliases_create Signed-off-by: lilinzhe --- README.md | 54 +++++ .../files/etc/inc/fauxapi/fauxapi_actions.inc | 29 ++- .../files/etc/inc/fauxapi/fauxapi_logger.inc | 64 ++--- .../inc/fauxapi/fauxapi_pfsense_interface.inc | 14 +- .../fauxapi_pfsense_interface_alias.inc | 218 ++++++++++++++++++ 5 files changed, 341 insertions(+), 38 deletions(-) create mode 100644 pfSense-pkg-FauxAPI/files/etc/inc/fauxapi/fauxapi_pfsense_interface_alias.inc diff --git a/README.md b/README.md index bc5f3b3..a92132d 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,7 @@ tasks feasible. - [system_stats](#user-content-system_stats) - Returns various useful system stats. - [system_info](#user-content-system_info) - Returns various useful system info. - [network_address_aliases_get](#user-content-network_address_aliases_get) - Returns address aliaes used by rules. + - [network_address_aliases_create](#user-content-network_address_aliases_create) - Creates An network aliaes for rules - [filter_rules_get](#user-content-filter_rules_get) - Returns firewall filters. @@ -969,6 +970,59 @@ curl \ } ``` --- +### network_address_aliases_create + - Create a address aliaes used by rules. Returns newest result + - HTTP: **POST** + - Params: none + - Request body: json + - **name** : name of aliases + - **type** : type of aliases. **MUST** be `network` for now. + - **cidr_addresses** : < list of > name alias what + - **address** an ip address or a network prefix. + - **details** a description of this address. for human readable documentation. + - **descr** : the description of current aliases. + - Response: json : the items after created + +*Example Request* +```bash +curl \ + -X GET \ + --silent \ + --insecure \ + --header "fauxapi-auth: " \ + --data '{"name": "wsdfan", "type": "network", "cidr_addresses": [{"address":"12.23.45.3/32", "details":"a"}], "descr":"Test"}' + "https:///fauxapi/v1/?action=network_address_aliases_create" +``` + +*Example Response* +```javascript +{ + "callid": "5e22393a9aa5a", + "action": "network_address_aliases_create", + "message": "ok", + "data": { + "aliases": { + "alias": [ + { + "name": "EasyRuleBlockHostsWAN", + "type": "network", + "address": "1.2.3.4/32 5.6.7.8/32", + "descr": "Hosts blocked from Firewall Log view", + "detail": "Entry added Fri, 27 Dec 2019 00:53:01 -0800||\u5df2\u6dfb\u52a0\u6761\u76ee Thu, 16 Jan 2020 03:42:37 -0800" + }, + { + "name": "wsdfan", + "descr": "Test", + "type": "network", + "address": "12.23.45.3/32", + "detail": "a" + } + ] + } + } +} +``` +--- ### filter_rules_get - Returns firewall filters. - HTTP: **GET** diff --git a/pfSense-pkg-FauxAPI/files/etc/inc/fauxapi/fauxapi_actions.inc b/pfSense-pkg-FauxAPI/files/etc/inc/fauxapi/fauxapi_actions.inc index 812fb1f..abf795e 100644 --- a/pfSense-pkg-FauxAPI/files/etc/inc/fauxapi/fauxapi_actions.inc +++ b/pfSense-pkg-FauxAPI/files/etc/inc/fauxapi/fauxapi_actions.inc @@ -451,7 +451,7 @@ class fauxApiActions { } /** - * address_aliases_get() + * network_address_aliases_get() * * @return boolean */ @@ -472,6 +472,33 @@ class fauxApiActions { ); return TRUE; } + /** + * address_aliases_get() + * + * @return boolean + */ + public function network_address_aliases_create() { + fauxApiLogger::debug(__METHOD__); + + $name = $this->action_input_data["name"]; + $type = $this->action_input_data["type"]; + $cidr_addresses =$this->action_input_data['cidr_addresses']; + $descr=$this->action_input_data['descr'] or "Added by fauxapi"; + + $alias = $this->PfsenseInterface->network_address_aliases_create($name, $type, $cidr_addresses, $descr); + + if (empty($alias)) { + $this->response->http_code = 500; + $this->response->message = 'unable to get address aliases'; + return FALSE; + } + $this->response->http_code = 200; + $this->response->message = 'ok'; + $this->response->data = array( + 'aliases' => $alias, + ); + return TRUE; + } /** * alias_update_urltables() diff --git a/pfSense-pkg-FauxAPI/files/etc/inc/fauxapi/fauxapi_logger.inc b/pfSense-pkg-FauxAPI/files/etc/inc/fauxapi/fauxapi_logger.inc index 39c3628..3cd85b3 100644 --- a/pfSense-pkg-FauxAPI/files/etc/inc/fauxapi/fauxapi_logger.inc +++ b/pfSense-pkg-FauxAPI/files/etc/inc/fauxapi/fauxapi_logger.inc @@ -1,4 +1,5 @@ fauxApiLogger::$log_count_limit) { + + if (count($__fauxapi_logs) > fauxApiLogger::$log_count_limit) { array_shift($__fauxapi_logs); } return $log; } - } diff --git a/pfSense-pkg-FauxAPI/files/etc/inc/fauxapi/fauxapi_pfsense_interface.inc b/pfSense-pkg-FauxAPI/files/etc/inc/fauxapi/fauxapi_pfsense_interface.inc index abe15af..cef3bd7 100644 --- a/pfSense-pkg-FauxAPI/files/etc/inc/fauxapi/fauxapi_pfsense_interface.inc +++ b/pfSense-pkg-FauxAPI/files/etc/inc/fauxapi/fauxapi_pfsense_interface.inc @@ -31,6 +31,7 @@ include_once '/etc/inc/pkg-utils.inc'; include_once '/usr/local/www/includes/functions.inc.php'; +include_once 'fauxapi_pfsense_interface_alias.inc'; class fauxApiPfsenseInterface { public $config_xml_root = 'pfsense'; @@ -775,17 +776,8 @@ class fauxApiPfsenseInterface { fauxApiLogger::debug(__METHOD__); return $config["filter"]["rule"]; } - - /** - * network_address_aliases_get() - * - * @return array - */ - public function network_address_aliases_get(){ - global $config; - fauxApiLogger::debug(__METHOD__); - return $config["aliases"]; - } + + use network_address_aliases; /** * function_call() diff --git a/pfSense-pkg-FauxAPI/files/etc/inc/fauxapi/fauxapi_pfsense_interface_alias.inc b/pfSense-pkg-FauxAPI/files/etc/inc/fauxapi/fauxapi_pfsense_interface_alias.inc new file mode 100644 index 0000000..e0eb4ba --- /dev/null +++ b/pfSense-pkg-FauxAPI/files/etc/inc/fauxapi/fauxapi_pfsense_interface_alias.inc @@ -0,0 +1,218 @@ += 32 || preg_match('/(^_*$|^\d*$|[^a-z0-9_])/i', $name)) { + return sprintf(gettext('The %1$s name must be less than 32 characters long, may not consist of only numbers, may not consist of only underscores, and may only contain the following characters: %2$s'), $object, 'a-z, A-Z, 0-9, _'); + } + if (in_array($name, $reserved, true)) { + return sprintf(gettext('The %1$s name must not be either of the reserved words %2$s or %3$s.'), $object, "'port'", "'pass'"); + } + if (getprotobyname($name)) { + return sprintf(gettext('The %1$s name must not be an IP protocol name such as TCP, UDP, ICMP etc.'), $object); + } + if (getservbyname($name, "tcp") || getservbyname($name, "udp")) { + return sprintf(gettext('The %1$s name must not be a well-known or registered TCP or UDP port name such as ssh, smtp, pop3, tftp, http, openvpn etc.'), $object); + } + + /* Check for reserved keyword names */ + foreach ($pf_reserved_keywords as $rk) { + if (strcasecmp($rk, $name) == 0) { + return sprintf(gettext("Cannot use a reserved keyword as an alias name: %s"), $rk); + } + } + + /* + * Packages (e.g. tinc) create interface groups, reserve this + * namespace pkg_ for them. + * One namespace is shared by Interfaces, Interface Groups and Aliases. + */ + if (substr($name, 0, 4) == 'pkg_') { + return gettext("The alias name cannot start with pkg_"); + } + + /* check for name interface description conflicts */ + foreach ($config['interfaces'] as $interface) { + if (strcasecmp($interface['descr'], $name) == 0) { + return gettext("An interface description with this name already exists."); + } + } + + /* Is the description already used as an interface group name? */ + if (is_array($config['ifgroups']['ifgroupentry'])) { + foreach ($config['ifgroups']['ifgroupentry'] as $ifgroupentry) { + if ($ifgroupentry['ifname'] == $name) { + return gettext("Sorry, an interface group with this name already exists."); + } + } + } + + // SHELL NOT BE A IP address. for prevent infinite loops make sure the alias name does not equal the value + if (is_ipaddr($name)) { + return "name shell not be ip address"; + } + // CHECK NAME + foreach ($config["aliases"]["alias"] as $cfgitem) { + if ($cfgitem["name"] == $name) { + return gettext("An alias with this name already exists."); + } + } + + return NULL; + } + + + + /** + * network_address_aliases_get() + * + * @return array + */ + public function network_address_aliases_get() + { + global $config; + fauxApiLogger::debug(__METHOD__); + return $config["aliases"]; + } + + + /** + * network_address_aliases_create() + * @param string $name - alias name + * @param string $type - alias type, supports "network" only + * @param array $cidr_addresses - alias CIDRAddress {"address": "1.2.3.4/32", "details":"message"} + * @param string $descr - alias descr, use for UI item. + * @return array + */ + public function network_address_aliases_create($name, $type, $cidr_addresses, $descr = "Added by fauxapi") + { + global $config; + fauxApiLogger::debug(__METHOD__, array( + 'name' => $name, 'type' => $type, 'cidr_addresses' => $cidr_addresses, 'descr' => $descr + )); + //TODO: change this if we need address_alias type to other. + if ($type != "network") { + $error_message = "not supported aliases type for now"; + $error_data = array('type' => $type); + fauxApiLogger::error($error_message, $error_data); + throw new \Exception($error_message); + } + $error_message = $this->is_valid_network_address_alias_name($name); + if ($error_message !== NULL) { + $error_data = array('name' => $name); + fauxApiLogger::error($error_message, $error_data); + throw new \Exception($error_message); + } + if (!is_array($cidr_addresses) || !$this->is_numeric_array($cidr_addresses) || count($cidr_addresses) == 0) { + $error_message = "must be a object array"; + $error_data = array('cidr_addresses' => $cidr_addresses); + fauxApiLogger::error($error_message, $error_data); + throw new \Exception($error_message); + } + $address_cfg = array(); + $details_cfg = array(); + foreach ($cidr_addresses as $addresscfg) { + $address = $addresscfg["address"]; + if (!is_ipaddr($address) && !is_subnet($address)) { + $error_message = "must be a address or subnet"; + $error_data = array('address' => $addresscfg); + fauxApiLogger::error($error_message, $error_data); + throw new \Exception($error_message); + } + $details = $addresscfg["details"]; + $details = preg_replace('/\|\|+/', '|', trim($details, "|")); + array_push($address_cfg, $address); + array_push($details_cfg, $details); + } + $address_cfg = join(" ", $address_cfg); + $details_cfg = join("||", $details_cfg); + # this shell saves to config. + $pconfig['name'] = $name; + $pconfig['descr'] = $descr; + $pconfig['type'] = $type; + $pconfig['address'] = $address_cfg; + $pconfig['detail'] = $details_cfg; + init_config_arr(array('aliases', 'alias')); + $currsize = count($config['aliases']['alias']); + $config["aliases"]['alias'][$currsize] = $pconfig; + $session_item = $_SESSION['Username']; + $_SESSION['Username'] = "admin"; // workaround to make writeconfig work + if (write_config(gettext("Edited a firewall alias."))) { + mark_subsystem_dirty('aliases'); + $_SESSION['Username'] = $session_item; + } else { + $_SESSION['Username'] = $session_item; + $error_message = "must be a address or subnet"; + $error_data = array('address' => $addresscfg); + fauxApiLogger::error($error_message, $error_data); + throw new \Exception($error_message); + } + + return $config["aliases"]; + } + + /** + * network_address_aliases_get() + * + * @return array + */ + public function network_address_aliases_update() + { + global $config; + fauxApiLogger::debug(__METHOD__); + return $config["aliases"]; + } + + /** + * network_address_aliases_get() + * + * @return array + */ + public function network_address_aliases_delete() + { + global $config; + fauxApiLogger::debug(__METHOD__); + return $config["aliases"]; + } +} From f7fb111a48ca2ba9c33808e43a0699dfe7cc4da9 Mon Sep 17 00:00:00 2001 From: lilinzhe Date: Sat, 18 Jan 2020 08:05:20 +0800 Subject: [PATCH 3/5] Alias Management: modify - network_address_aliases_update Signed-off-by: lilinzhe --- README.md | 54 +++++ .../files/etc/inc/fauxapi/fauxapi_actions.inc | 32 ++- .../fauxapi_pfsense_interface_alias.inc | 197 +++++------------- .../fauxapi_pfsense_interface_alias.priv.inc | 148 +++++++++++++ 4 files changed, 289 insertions(+), 142 deletions(-) create mode 100644 pfSense-pkg-FauxAPI/files/etc/inc/fauxapi/fauxapi_pfsense_interface_alias.priv.inc diff --git a/README.md b/README.md index a92132d..0d96dc7 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,7 @@ tasks feasible. - [system_info](#user-content-system_info) - Returns various useful system info. - [network_address_aliases_get](#user-content-network_address_aliases_get) - Returns address aliaes used by rules. - [network_address_aliases_create](#user-content-network_address_aliases_create) - Creates An network aliaes for rules + - [network_address_aliases_update](#user-content-network_address_aliases_update) - Update a address aliaes. Returns newest result - [filter_rules_get](#user-content-filter_rules_get) - Returns firewall filters. @@ -994,6 +995,59 @@ curl \ "https:///fauxapi/v1/?action=network_address_aliases_create" ``` +*Example Response* +```javascript +{ + "callid": "5e22393a9aa5a", + "action": "network_address_aliases_create", + "message": "ok", + "data": { + "aliases": { + "alias": [ + { + "name": "EasyRuleBlockHostsWAN", + "type": "network", + "address": "1.2.3.4/32 5.6.7.8/32", + "descr": "Hosts blocked from Firewall Log view", + "detail": "Entry added Fri, 27 Dec 2019 00:53:01 -0800||\u5df2\u6dfb\u52a0\u6761\u76ee Thu, 16 Jan 2020 03:42:37 -0800" + }, + { + "name": "wsdfan", + "descr": "Test", + "type": "network", + "address": "12.23.45.3/32", + "detail": "a" + } + ] + } + } +} +``` +--- +### network_address_aliases_update + - Update a address aliaes. Returns newest result + - HTTP: **POST** + - Params: none + - Request body: json + - **name** : name of aliases. identiy which aliases frr modify + - **type** : type of aliases. **MUST** be `network` for now. + - **cidr_addresses** : < list of > name alias what + - **address** an ip address or a network prefix. + - **details** a description of this address. for human readable documentation. + - **descr** : the description of current aliases. + - Response: json : the items after created + +*Example Request* +```bash +curl \ + -X GET \ + --silent \ + --insecure \ + --header "fauxapi-auth: " \ + --data '{"name": "wsdfan", "type": "network", "cidr_addresses": [{"address":"12.23.45.3/32", "details":"a"}], "descr":"Test"}' + "https:///fauxapi/v1/?action=network_address_aliases_create" +``` + *Example Response* ```javascript { diff --git a/pfSense-pkg-FauxAPI/files/etc/inc/fauxapi/fauxapi_actions.inc b/pfSense-pkg-FauxAPI/files/etc/inc/fauxapi/fauxapi_actions.inc index abf795e..21efa81 100644 --- a/pfSense-pkg-FauxAPI/files/etc/inc/fauxapi/fauxapi_actions.inc +++ b/pfSense-pkg-FauxAPI/files/etc/inc/fauxapi/fauxapi_actions.inc @@ -473,7 +473,7 @@ class fauxApiActions { return TRUE; } /** - * address_aliases_get() + * network_address_aliases_create() * * @return boolean */ @@ -483,7 +483,7 @@ class fauxApiActions { $name = $this->action_input_data["name"]; $type = $this->action_input_data["type"]; $cidr_addresses =$this->action_input_data['cidr_addresses']; - $descr=$this->action_input_data['descr'] or "Added by fauxapi"; + $descr=$this->action_input_data['descr']; $alias = $this->PfsenseInterface->network_address_aliases_create($name, $type, $cidr_addresses, $descr); @@ -499,6 +499,34 @@ class fauxApiActions { ); return TRUE; } + + /** + * network_address_aliases_update() + * + * @return boolean + */ + public function network_address_aliases_update() { + fauxApiLogger::debug(__METHOD__); + + $name = $this->action_input_data["name"]; + $type = $this->action_input_data["type"]; + $cidr_addresses =$this->action_input_data['cidr_addresses']; + $descr=$this->action_input_data['descr'] ; + + $alias = $this->PfsenseInterface->network_address_aliases_update($name, $type, $cidr_addresses, $descr); + + if (empty($alias)) { + $this->response->http_code = 500; + $this->response->message = 'unable to get address aliases'; + return FALSE; + } + $this->response->http_code = 200; + $this->response->message = 'ok'; + $this->response->data = array( + 'aliases' => $alias, + ); + return TRUE; + } /** * alias_update_urltables() diff --git a/pfSense-pkg-FauxAPI/files/etc/inc/fauxapi/fauxapi_pfsense_interface_alias.inc b/pfSense-pkg-FauxAPI/files/etc/inc/fauxapi/fauxapi_pfsense_interface_alias.inc index e0eb4ba..d79c9b8 100644 --- a/pfSense-pkg-FauxAPI/files/etc/inc/fauxapi/fauxapi_pfsense_interface_alias.inc +++ b/pfSense-pkg-FauxAPI/files/etc/inc/fauxapi/fauxapi_pfsense_interface_alias.inc @@ -2,108 +2,9 @@ namespace fauxapi\v1; -// write_config requires functions from this -include '/etc/inc/phpsessionmanager.inc'; -include '/etc/inc/auth.inc'; +include 'fauxapi_pfsense_interface_alias.priv.inc'; trait network_address_aliases { - - /* - From PFSENSE. copyed for old version. - If $return_message is true then - returns a text message about the reason that the name is invalid. - the text includes the type of "thing" that is being checked, passed in $object. (e.g. "alias", "gateway group", "schedule") - else - returns true if $name is a valid name for an alias - returns false if $name is not a valid name for an alias - - Aliases cannot be: - bad chars: anything except a-z 0-9 and underscore - bad names: empty string, pure numeric, pure underscore - reserved words: pre-defined service/protocol/port names which should not be ambiguous, and the words "port" and "pass" */ - - private function is_valid_network_address_alias_name($name) - { - global $config; - global $pf_reserved_keywords; - $reserved_table_names = array( - "bogons", - "bogonsv6", - "negate_networks", - "snort2c", - "sshguard", - "tonatsubnets", - "virusprot", - "vpn_networks", - ); - $reserved_ifs = get_configured_interface_list(true); - $pf_reserved_keywords = array_merge($pf_reserved_keywords, $reserved_ifs, $reserved_table_names); - - $object = "alias"; - /* Array of reserved words */ - $reserved = array("port", "pass"); - - if (!is_string($name) || strlen($name) >= 32 || preg_match('/(^_*$|^\d*$|[^a-z0-9_])/i', $name)) { - return sprintf(gettext('The %1$s name must be less than 32 characters long, may not consist of only numbers, may not consist of only underscores, and may only contain the following characters: %2$s'), $object, 'a-z, A-Z, 0-9, _'); - } - if (in_array($name, $reserved, true)) { - return sprintf(gettext('The %1$s name must not be either of the reserved words %2$s or %3$s.'), $object, "'port'", "'pass'"); - } - if (getprotobyname($name)) { - return sprintf(gettext('The %1$s name must not be an IP protocol name such as TCP, UDP, ICMP etc.'), $object); - } - if (getservbyname($name, "tcp") || getservbyname($name, "udp")) { - return sprintf(gettext('The %1$s name must not be a well-known or registered TCP or UDP port name such as ssh, smtp, pop3, tftp, http, openvpn etc.'), $object); - } - - /* Check for reserved keyword names */ - foreach ($pf_reserved_keywords as $rk) { - if (strcasecmp($rk, $name) == 0) { - return sprintf(gettext("Cannot use a reserved keyword as an alias name: %s"), $rk); - } - } - - /* - * Packages (e.g. tinc) create interface groups, reserve this - * namespace pkg_ for them. - * One namespace is shared by Interfaces, Interface Groups and Aliases. - */ - if (substr($name, 0, 4) == 'pkg_') { - return gettext("The alias name cannot start with pkg_"); - } - - /* check for name interface description conflicts */ - foreach ($config['interfaces'] as $interface) { - if (strcasecmp($interface['descr'], $name) == 0) { - return gettext("An interface description with this name already exists."); - } - } - - /* Is the description already used as an interface group name? */ - if (is_array($config['ifgroups']['ifgroupentry'])) { - foreach ($config['ifgroups']['ifgroupentry'] as $ifgroupentry) { - if ($ifgroupentry['ifname'] == $name) { - return gettext("Sorry, an interface group with this name already exists."); - } - } - } - - // SHELL NOT BE A IP address. for prevent infinite loops make sure the alias name does not equal the value - if (is_ipaddr($name)) { - return "name shell not be ip address"; - } - // CHECK NAME - foreach ($config["aliases"]["alias"] as $cfgitem) { - if ($cfgitem["name"] == $name) { - return gettext("An alias with this name already exists."); - } - } - - return NULL; - } - - - /** * network_address_aliases_get() * @@ -138,7 +39,7 @@ trait network_address_aliases fauxApiLogger::error($error_message, $error_data); throw new \Exception($error_message); } - $error_message = $this->is_valid_network_address_alias_name($name); + $error_message = fauxApiInterfaceAliasTools::is_valid_network_address_alias_name($name); if ($error_message !== NULL) { $error_data = array('name' => $name); fauxApiLogger::error($error_message, $error_data); @@ -150,69 +51,85 @@ trait network_address_aliases fauxApiLogger::error($error_message, $error_data); throw new \Exception($error_message); } - $address_cfg = array(); - $details_cfg = array(); - foreach ($cidr_addresses as $addresscfg) { - $address = $addresscfg["address"]; - if (!is_ipaddr($address) && !is_subnet($address)) { - $error_message = "must be a address or subnet"; - $error_data = array('address' => $addresscfg); - fauxApiLogger::error($error_message, $error_data); - throw new \Exception($error_message); - } - $details = $addresscfg["details"]; - $details = preg_replace('/\|\|+/', '|', trim($details, "|")); - array_push($address_cfg, $address); - array_push($details_cfg, $details); - } - $address_cfg = join(" ", $address_cfg); - $details_cfg = join("||", $details_cfg); + $result = fauxApiInterfaceAliasTools::parse_cidr_addresslist_to_config($cidr_addresses); # this shell saves to config. $pconfig['name'] = $name; $pconfig['descr'] = $descr; $pconfig['type'] = $type; - $pconfig['address'] = $address_cfg; - $pconfig['detail'] = $details_cfg; + $pconfig['address'] = $result["address"]; + $pconfig['detail'] = $result["detail"]; init_config_arr(array('aliases', 'alias')); $currsize = count($config['aliases']['alias']); $config["aliases"]['alias'][$currsize] = $pconfig; - $session_item = $_SESSION['Username']; - $_SESSION['Username'] = "admin"; // workaround to make writeconfig work - if (write_config(gettext("Edited a firewall alias."))) { - mark_subsystem_dirty('aliases'); - $_SESSION['Username'] = $session_item; - } else { - $_SESSION['Username'] = $session_item; - $error_message = "must be a address or subnet"; - $error_data = array('address' => $addresscfg); - fauxApiLogger::error($error_message, $error_data); - throw new \Exception($error_message); + if (!fauxApiInterfaceAliasTools::write_config_aliases()) { + return NULL; } - return $config["aliases"]; } /** - * network_address_aliases_get() + * network_address_aliases_update() * + * @param string $name - which alias to modify + * @param string $type - alias type, supports "network" only + * @param array $cidr_addresses - alias CIDRAddress {"address": "1.2.3.4/32", "details":"message"} + * @param string $descr - alias descr, use for UI item. * @return array */ - public function network_address_aliases_update() + public function network_address_aliases_update($name, $type, $cidr_addresses, $descr = "Added by fauxapi") { global $config; - fauxApiLogger::debug(__METHOD__); + fauxApiLogger::debug(__METHOD__, array( + 'name' => $name, 'type' => $type, 'cidr_addresses' => $cidr_addresses, 'descr' => $descr + )); + $pconfig=NULL; + for ($id = 0; $id < count($config["aliases"]["alias"]); $id+=1) { + $cfgitem = $config["aliases"]["alias"][$id]; + if ($cfgitem["name"] == $name){ + $pconfig=&$config["aliases"]["alias"][$id]; + } + } + if ($pconfig== NULL){ + //not find + $error_message = "not find name"; + $error_data = array('name' => $name); + fauxApiLogger::error($error_message, $error_data); + throw new \Exception($error_message); + } + if ($type != "network") { + $error_message = "can support type network only for now"; + $error_data = array('type' => $type); + fauxApiLogger::error($error_message, $error_data); + throw new \Exception($error_message); + } + $result = fauxApiInterfaceAliasTools::parse_cidr_addresslist_to_config($cidr_addresses); + # this shell saves to config. + $pconfig['name'] = $name; + $pconfig['descr'] = $descr; + $pconfig['type'] = $type; + $pconfig['address'] = $result["address"]; + $pconfig['detail'] = $result["detail"]; + if (!fauxApiInterfaceAliasTools::write_config_aliases()) { + return NULL; + } + return $config["aliases"]; } - /** - * network_address_aliases_get() + * network_address_aliases_delete() * + * @param string $name - which alias to modify * @return array */ - public function network_address_aliases_delete() + public function network_address_aliases_delete($name) { global $config; - fauxApiLogger::debug(__METHOD__); - return $config["aliases"]; + fauxApiLogger::debug(__METHOD__, array( + 'name' => $name + )); + $pconfig=NULL; + for ($id = 0; $id < count($config["aliases"]["alias"]); $id+=1) { + } + } } diff --git a/pfSense-pkg-FauxAPI/files/etc/inc/fauxapi/fauxapi_pfsense_interface_alias.priv.inc b/pfSense-pkg-FauxAPI/files/etc/inc/fauxapi/fauxapi_pfsense_interface_alias.priv.inc new file mode 100644 index 0000000..3769d2d --- /dev/null +++ b/pfSense-pkg-FauxAPI/files/etc/inc/fauxapi/fauxapi_pfsense_interface_alias.priv.inc @@ -0,0 +1,148 @@ += 32 || preg_match('/(^_*$|^\d*$|[^a-z0-9_])/i', $name)) { + return sprintf(gettext('The %1$s name must be less than 32 characters long, may not consist of only numbers, may not consist of only underscores, and may only contain the following characters: %2$s'), $object, 'a-z, A-Z, 0-9, _'); + } + if (in_array($name, $reserved, true)) { + return sprintf(gettext('The %1$s name must not be either of the reserved words %2$s or %3$s.'), $object, "'port'", "'pass'"); + } + if (getprotobyname($name)) { + return sprintf(gettext('The %1$s name must not be an IP protocol name such as TCP, UDP, ICMP etc.'), $object); + } + if (getservbyname($name, "tcp") || getservbyname($name, "udp")) { + return sprintf(gettext('The %1$s name must not be a well-known or registered TCP or UDP port name such as ssh, smtp, pop3, tftp, http, openvpn etc.'), $object); + } + + /* Check for reserved keyword names */ + foreach ($pf_reserved_keywords as $rk) { + if (strcasecmp($rk, $name) == 0) { + return sprintf(gettext("Cannot use a reserved keyword as an alias name: %s"), $rk); + } + } + + /* + * Packages (e.g. tinc) create interface groups, reserve this + * namespace pkg_ for them. + * One namespace is shared by Interfaces, Interface Groups and Aliases. + */ + if (substr($name, 0, 4) == 'pkg_') { + return gettext("The alias name cannot start with pkg_"); + } + + /* check for name interface description conflicts */ + foreach ($config['interfaces'] as $interface) { + if (strcasecmp($interface['descr'], $name) == 0) { + return gettext("An interface description with this name already exists."); + } + } + + /* Is the description already used as an interface group name? */ + if (is_array($config['ifgroups']['ifgroupentry'])) { + foreach ($config['ifgroups']['ifgroupentry'] as $ifgroupentry) { + if ($ifgroupentry['ifname'] == $name) { + return gettext("Sorry, an interface group with this name already exists."); + } + } + } + + // SHELL NOT BE A IP address. for prevent infinite loops make sure the alias name does not equal the value + if (is_ipaddr($name)) { + return "name shell not be ip address"; + } + // CHECK NAME + foreach ($config["aliases"]["alias"] as $cfgitem) { + if ($cfgitem["name"] == $name) { + return gettext("An alias with this name already exists."); + } + } + + return NULL; + } + + public static function parse_cidr_addresslist_to_config($cidr_addresses) { + $address_cfg = array(); + $details_cfg = array(); + foreach ($cidr_addresses as $addresscfg) { + $address = $addresscfg["address"]; + if (!is_ipaddr($address) && !is_subnet($address)) { + $error_message = "must be a address or subnet"; + $error_data = array('address' => $addresscfg); + fauxApiLogger::error($error_message, $error_data); + throw new \Exception($error_message); + } + $details = $addresscfg["details"]; + $details = preg_replace('/\|\|+/', '|', trim($details, "|")); + array_push($address_cfg, $address); + array_push($details_cfg, $details); + } + $address_cfg = join(" ", $address_cfg); + $details_cfg = join("||", $details_cfg); + return array( + "address"=>$address_cfg, + "detail"=>$details, + ); + } + public static function write_config_aliases() { + global $config; + $session_item = $_SESSION['Username']; + $_SESSION['Username'] = "admin"; // workaround to make writeconfig work + if (write_config(gettext("Edited a firewall alias."))) { + mark_subsystem_dirty('aliases'); + $_SESSION['Username'] = $session_item; + return true; + } else{ + $_SESSION['Username'] = $session_item; + $error_message = "write config failed"; + $error_data = array('newcfg' => $config["aliases"]['alias']); + fauxApiLogger::error($error_message, $error_data); + throw new \Exception($error_message); + } + } + + +} + +?> \ No newline at end of file From ab2542ea2dbb14d8eafe452e6f800a0fc1267bb5 Mon Sep 17 00:00:00 2001 From: lilinzhe Date: Sat, 18 Jan 2020 09:29:35 +0800 Subject: [PATCH 4/5] Alias Management: delete - network_address_aliases_delete Signed-off-by: lilinzhe --- README.md | 46 ++++++++++- .../files/etc/inc/fauxapi/fauxapi_actions.inc | 25 ++++++ .../fauxapi_pfsense_interface_alias.inc | 25 +++++- .../fauxapi_pfsense_interface_alias.priv.inc | 77 +++++++++++++++++++ 4 files changed, 170 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 0d96dc7..0a01bed 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,7 @@ tasks feasible. - [network_address_aliases_get](#user-content-network_address_aliases_get) - Returns address aliaes used by rules. - [network_address_aliases_create](#user-content-network_address_aliases_create) - Creates An network aliaes for rules - [network_address_aliases_update](#user-content-network_address_aliases_update) - Update a address aliaes. Returns newest result + - [network_address_aliases_delete](#user-content-network_address_aliases_delete) - delete a address aliaes. Returns newest result - [filter_rules_get](#user-content-filter_rules_get) - Returns firewall filters. @@ -992,14 +993,14 @@ curl \ --insecure \ --header "fauxapi-auth: " \ --data '{"name": "wsdfan", "type": "network", "cidr_addresses": [{"address":"12.23.45.3/32", "details":"a"}], "descr":"Test"}' - "https:///fauxapi/v1/?action=network_address_aliases_create" + "https:///fauxapi/v1/?action=network_address_aliases_update" ``` *Example Response* ```javascript { "callid": "5e22393a9aa5a", - "action": "network_address_aliases_create", + "action": "network_address_aliases_update", "message": "ok", "data": { "aliases": { @@ -1077,6 +1078,47 @@ curl \ } ``` --- +### network_address_aliases_delete + - deletes a address aliaes. Returns newest result + - HTTP: **POST** + - Params: none + - Request body: json + - **name** : name of aliases. identiy which aliase to delete + - Response: json : the items after created + +*Example Request* +```bash +curl \ + -X GET \ + --silent \ + --insecure \ + --header "fauxapi-auth: " \ + --data '{"name": "wsdfan"}' + "https:///fauxapi/v1/?action=network_address_aliases_delete" +``` + +*Example Response* +```javascript +{ + "callid": "5e22393a9aa5a", + "action": "network_address_aliases_delete", + "message": "ok", + "data": { + "aliases": { + "alias": [ + { + "name": "EasyRuleBlockHostsWAN", + "type": "network", + "address": "1.2.3.4/32 5.6.7.8/32", + "descr": "Hosts blocked from Firewall Log view", + "detail": "Entry added Fri, 27 Dec 2019 00:53:01 -0800||\u5df2\u6dfb\u52a0\u6761\u76ee Thu, 16 Jan 2020 03:42:37 -0800" + } + ] + } + } +} +``` +--- ### filter_rules_get - Returns firewall filters. - HTTP: **GET** diff --git a/pfSense-pkg-FauxAPI/files/etc/inc/fauxapi/fauxapi_actions.inc b/pfSense-pkg-FauxAPI/files/etc/inc/fauxapi/fauxapi_actions.inc index 21efa81..ee10624 100644 --- a/pfSense-pkg-FauxAPI/files/etc/inc/fauxapi/fauxapi_actions.inc +++ b/pfSense-pkg-FauxAPI/files/etc/inc/fauxapi/fauxapi_actions.inc @@ -527,6 +527,31 @@ class fauxApiActions { ); return TRUE; } + + /** + * network_address_aliases_update() + * + * @return boolean + */ + public function network_address_aliases_delete() { + fauxApiLogger::debug(__METHOD__); + + $name = $this->action_input_data["name"]; + + $alias = $this->PfsenseInterface->network_address_aliases_delete($name); + + if (empty($alias)) { + $this->response->http_code = 500; + $this->response->message = 'unable to get address aliases'; + return FALSE; + } + $this->response->http_code = 200; + $this->response->message = 'ok'; + $this->response->data = array( + 'aliases' => $alias, + ); + return TRUE; + } /** * alias_update_urltables() diff --git a/pfSense-pkg-FauxAPI/files/etc/inc/fauxapi/fauxapi_pfsense_interface_alias.inc b/pfSense-pkg-FauxAPI/files/etc/inc/fauxapi/fauxapi_pfsense_interface_alias.inc index d79c9b8..9aafd31 100644 --- a/pfSense-pkg-FauxAPI/files/etc/inc/fauxapi/fauxapi_pfsense_interface_alias.inc +++ b/pfSense-pkg-FauxAPI/files/etc/inc/fauxapi/fauxapi_pfsense_interface_alias.inc @@ -127,9 +127,32 @@ trait network_address_aliases fauxApiLogger::debug(__METHOD__, array( 'name' => $name )); - $pconfig=NULL; + $idx = -1; for ($id = 0; $id < count($config["aliases"]["alias"]); $id+=1) { + if ($config["aliases"]["alias"][$id]['name'] == $name) { + $idx=$id; + break; + } + } + if ($idx == -1) { + //not find + $error_message = "not find name"; + $error_data = array('name' => $name); + fauxApiLogger::error($error_message, $error_data); + throw new \Exception($error_message); + } + $usederrmsg = fauxApiInterfaceAliasTools::is_alias_used($name); + if ($usederrmsg) { + $error_message = $usederrmsg; + $error_data = array('name'=>$name); + fauxApiLogger::error($error_message, $error_data); + throw new \Exception($error_message); + } + \array_splice($config["aliases"]["alias"], $idx, 1); + if (!fauxApiInterfaceAliasTools::write_config_aliases()) { + return NULL; } + return $config["aliases"]; } } diff --git a/pfSense-pkg-FauxAPI/files/etc/inc/fauxapi/fauxapi_pfsense_interface_alias.priv.inc b/pfSense-pkg-FauxAPI/files/etc/inc/fauxapi/fauxapi_pfsense_interface_alias.priv.inc index 3769d2d..9016599 100644 --- a/pfSense-pkg-FauxAPI/files/etc/inc/fauxapi/fauxapi_pfsense_interface_alias.priv.inc +++ b/pfSense-pkg-FauxAPI/files/etc/inc/fauxapi/fauxapi_pfsense_interface_alias.priv.inc @@ -142,6 +142,83 @@ class fauxApiInterfaceAliasTools } } + public static function find_alias_reference($section, $field, $origname, &$is_alias_referenced, &$referenced_by) { + global $config; + if (!$origname || $is_alias_referenced) { + return; + } + + $sectionref = &$config; + foreach ($section as $sectionname) { + if (is_array($sectionref) && isset($sectionref[$sectionname])) { + $sectionref = &$sectionref[$sectionname]; + } else { + return; + } + } + + if (is_array($sectionref)) { + foreach ($sectionref as $itemkey => $item) { + $fieldfound = true; + $fieldref = &$sectionref[$itemkey]; + foreach ($field as $fieldname) { + if (is_array($fieldref) && isset($fieldref[$fieldname])) { + $fieldref = &$fieldref[$fieldname]; + } else { + $fieldfound = false; + break; + } + } + if ($fieldfound && $fieldref == $origname) { + $is_alias_referenced = true; + if (is_array($item)) { + $referenced_by = $item['descr']; + } + break; + } + } + } + } + + public static function is_alias_used($alias_name){ + $is_alias_referenced = false; + $referenced_by = false; + + // Firewall rules + fauxApiInterfaceAliasTools::find_alias_reference(array('filter', 'rule'), array('source', 'address'), $alias_name, $is_alias_referenced, $referenced_by); + fauxApiInterfaceAliasTools::find_alias_reference(array('filter', 'rule'), array('destination', 'address'), $alias_name, $is_alias_referenced, $referenced_by); + fauxApiInterfaceAliasTools::find_alias_reference(array('filter', 'rule'), array('source', 'port'), $alias_name, $is_alias_referenced, $referenced_by); + fauxApiInterfaceAliasTools::find_alias_reference(array('filter', 'rule'), array('destination', 'port'), $alias_name, $is_alias_referenced, $referenced_by); + // NAT Rules + fauxApiInterfaceAliasTools::find_alias_reference(array('nat', 'rule'), array('source', 'address'), $alias_name, $is_alias_referenced, $referenced_by); + fauxApiInterfaceAliasTools::find_alias_reference(array('nat', 'rule'), array('source', 'port'), $alias_name, $is_alias_referenced, $referenced_by); + fauxApiInterfaceAliasTools::find_alias_reference(array('nat', 'rule'), array('destination', 'address'), $alias_name, $is_alias_referenced, $referenced_by); + fauxApiInterfaceAliasTools::find_alias_reference(array('nat', 'rule'), array('destination', 'port'), $alias_name, $is_alias_referenced, $referenced_by); + fauxApiInterfaceAliasTools::find_alias_reference(array('nat', 'rule'), array('target'), $alias_name, $is_alias_referenced, $referenced_by); + fauxApiInterfaceAliasTools::find_alias_reference(array('nat', 'rule'), array('local-port'), $alias_name, $is_alias_referenced, $referenced_by); + // NAT 1:1 Rules + //fauxApiInterfaceAliasTools::find_alias_reference(array('nat', 'onetoone'), array('external'), $alias_name, $is_alias_referenced, $referenced_by); + //fauxApiInterfaceAliasTools::find_alias_reference(array('nat', 'onetoone'), array('source', 'address'), $alias_name, $is_alias_referenced, $referenced_by); + fauxApiInterfaceAliasTools::find_alias_reference(array('nat', 'onetoone'), array('destination', 'address'), $alias_name, $is_alias_referenced, $referenced_by); + // NAT Outbound Rules + fauxApiInterfaceAliasTools::find_alias_reference(array('nat', 'outbound', 'rule'), array('source', 'network'), $alias_name, $is_alias_referenced, $referenced_by); + fauxApiInterfaceAliasTools::find_alias_reference(array('nat', 'outbound', 'rule'), array('sourceport'), $alias_name, $is_alias_referenced, $referenced_by); + fauxApiInterfaceAliasTools::find_alias_reference(array('nat', 'outbound', 'rule'), array('destination', 'address'), $alias_name, $is_alias_referenced, $referenced_by); + fauxApiInterfaceAliasTools::find_alias_reference(array('nat', 'outbound', 'rule'), array('dstport'), $alias_name, $is_alias_referenced, $referenced_by); + fauxApiInterfaceAliasTools::find_alias_reference(array('nat', 'outbound', 'rule'), array('target'), $alias_name, $is_alias_referenced, $referenced_by); + // Alias in an alias + fauxApiInterfaceAliasTools::find_alias_reference(array('aliases', 'alias'), array('address'), $alias_name, $is_alias_referenced, $referenced_by); + // Load Balancer + fauxApiInterfaceAliasTools::find_alias_reference(array('load_balancer', 'lbpool'), array('port'), $alias_name, $is_alias_referenced, $referenced_by); + fauxApiInterfaceAliasTools::find_alias_reference(array('load_balancer', 'virtual_server'), array('port'), $alias_name, $is_alias_referenced, $referenced_by); + // Static routes + fauxApiInterfaceAliasTools::find_alias_reference(array('staticroutes', 'route'), array('network'), $alias_name, $is_alias_referenced, $referenced_by); + if ($is_alias_referenced) { + return $referenced_by; + } + return NULL; + } + } From 8367013034715bbc446d4fc15ac279dbbba81f00 Mon Sep 17 00:00:00 2001 From: lilinzhe Date: Sun, 19 Jan 2020 14:05:29 +0800 Subject: [PATCH 5/5] Filter Rules Management: filter_rules_create / filter_rules_delele Signed-off-by: lilinzhe --- README.md | 88 +- .../files/etc/inc/fauxapi/fauxapi_actions.inc | 60 ++ .../inc/fauxapi/fauxapi_pfsense_interface.inc | 13 +- .../fauxapi_pfsense_interface_alias.priv.inc | 4 +- ...fauxapi_pfsense_interface_filter_rules.inc | 63 ++ ...pi_pfsense_interface_filter_rules.priv.inc | 776 ++++++++++++++++++ 6 files changed, 980 insertions(+), 24 deletions(-) create mode 100644 pfSense-pkg-FauxAPI/files/etc/inc/fauxapi/fauxapi_pfsense_interface_filter_rules.inc create mode 100644 pfSense-pkg-FauxAPI/files/etc/inc/fauxapi/fauxapi_pfsense_interface_filter_rules.priv.inc diff --git a/README.md b/README.md index 0a01bed..0f07790 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,8 @@ tasks feasible. - [network_address_aliases_update](#user-content-network_address_aliases_update) - Update a address aliaes. Returns newest result - [network_address_aliases_delete](#user-content-network_address_aliases_delete) - delete a address aliaes. Returns newest result - [filter_rules_get](#user-content-filter_rules_get) - Returns firewall filters. + - [filter_rules_create](#user-content-filter_rules_create) - Creates firewall filters. + - [filter_rules_delete](#user-content-filter_rules_delete) - Deletes firewall filters. ## Approach @@ -977,13 +979,13 @@ curl \ - HTTP: **POST** - Params: none - Request body: json - - **name** : name of aliases - - **type** : type of aliases. **MUST** be `network` for now. - - **cidr_addresses** : < list of > name alias what + - **name** :< string > name of aliases + - **type** :< string > type of aliases. **MUST** be `network` for now. + - **cidr_addresses** : < list of < object > > name alias what - **address** an ip address or a network prefix. - **details** a description of this address. for human readable documentation. - - **descr** : the description of current aliases. - - Response: json : the items after created + - **descr** : < string > the description of current aliases. + - Response: json < object >: the items after created *Example Request* ```bash @@ -1030,13 +1032,13 @@ curl \ - HTTP: **POST** - Params: none - Request body: json - - **name** : name of aliases. identiy which aliases frr modify - - **type** : type of aliases. **MUST** be `network` for now. - - **cidr_addresses** : < list of > name alias what + - **name** :< string > name of aliases. identiy which aliases frr modify + - **type** :< string > type of aliases. **MUST** be `network` for now. + - **cidr_addresses** : < list of < object > > name alias what - **address** an ip address or a network prefix. - **details** a description of this address. for human readable documentation. - - **descr** : the description of current aliases. - - Response: json : the items after created + - **descr** : < string > the description of current aliases. + - Response: json < object >: the items after created *Example Request* ```bash @@ -1083,8 +1085,8 @@ curl \ - HTTP: **POST** - Params: none - Request body: json - - **name** : name of aliases. identiy which aliase to delete - - Response: json : the items after created + - **name** :< string > name of aliases. identiy which aliase to delete + - Response: json < object >: the items after created *Example Request* ```bash @@ -1210,6 +1212,68 @@ curl \ } } ``` +--- +### filter_rules_create + - Creates firewall filters + - HTTP: **POST** + - Params: none + - Request body: json + - **position**: < int >: insert to which position. + - **rule**: < object >: what is the rule. + - **type** :< string > : Type of filter. could take value: pass / block / reject + - **ipprotocol**: < string >: Which network type? could take value: inet / inet6 / inet46 + - **protocol**: < string >: if seted. could only take value: tcp. used for port match. + - **descr** : < string > : Used for description. + - **interface**: < string >: To which interface. e.g. WAN + - **source**: < object > : match source item. + - `{"any":""}`: matchs any address. + - `{"address": "network_address_aliases"}`: matchs any network_address_aliases. + - `{"address": "1.2.3.4"}`: matchs address 1.2.3.4 + - `{"any":"", "port": "443-1000"}`: matchs 443 to 1000 port. uses with protocol + - **destination**: < object >: match description. -- same as above. + - Response: json < object >: the items after created + +*Test it carefully before going to wild please. USE AT YOUR OWN RISK* + +*Example Request* +```bash +curl \ + -X POST \ + --silent \ + --insecure \ + --header "fauxapi-auth: " \ + --data '{"position": 1, "rule": {"type": "reject", "ipprotocol": "inet", "descr": "testobject", "interface": "wan", "source": {"any": ""}, "destination": {"address": "1.2.3.4"}}}' \ + "https:///fauxapi/v1/?action=filter_rules_create" +``` +*Example Response* +Same As [filter_rules_get](#user-content-filter_rules_get) + +--- +### filter_rules_delete + - Returns firewall filters. + - HTTP: **POST** + - Params: none + - Request body: json + - **position**: : deletes which position. + +*Test it carefully before going to wild please. USE AT YOUR OWN RISK* + +Because there's nothing like Unique ID or name in rule. Currently we could only take the position to identify which rule shell be deleted. + +*Example Request* +```bash +curl \ + -X POST \ + --silent \ + --insecure \ + --header "fauxapi-auth: " \ + --data '{"position": 1}' \ + "https:///fauxapi/v1/?action=filter_rules_delete" +``` + +*Example Response* +Same As [filter_rules_get](#user-content-filter_rules_get) + --- ## Versions and Testing diff --git a/pfSense-pkg-FauxAPI/files/etc/inc/fauxapi/fauxapi_actions.inc b/pfSense-pkg-FauxAPI/files/etc/inc/fauxapi/fauxapi_actions.inc index ee10624..7f25e18 100644 --- a/pfSense-pkg-FauxAPI/files/etc/inc/fauxapi/fauxapi_actions.inc +++ b/pfSense-pkg-FauxAPI/files/etc/inc/fauxapi/fauxapi_actions.inc @@ -450,6 +450,66 @@ class fauxApiActions { return TRUE; } + /** + * filter_rules_create() + * + * @return boolean + */ + public function filter_rules_create() { + fauxApiLogger::debug(__METHOD__); + + $position = $this->action_input_data["position"]; + $ruleobj = $this->action_input_data["rule"]; + $rules = $this->PfsenseInterface->filter_rules_create($position, $ruleobj); + + if (empty($rules)) { + $this->response->http_code = 500; + $this->response->message = 'unable to get filter rule(s)'; + return FALSE; + } + $this->response->http_code = 200; + $this->response->message = 'ok'; + $this->response->data = array( + 'filter' => array( + 'rules' => $rules + ), + ); + return TRUE; + } + + /** + * filter_rules_create() + * + * @return boolean + */ + public function filter_rules_delete() { + fauxApiLogger::debug(__METHOD__); + + if(!isset($this->action_input_data["position"])) { + $error_message = "could only delete by position at now"; + $error_data = array('postdata' => $this->action_input_data); + fauxApiLogger::error($error_message, $error_data); + throw new \Exception($error_message); + return FALSE; + } + $position = $this->action_input_data["position"]; + $rules = $this->PfsenseInterface->filter_rules_delete_by_position($position); + + if (empty($rules)) { + $this->response->http_code = 500; + $this->response->message = 'unable to get filter rule(s)'; + return FALSE; + } + $this->response->http_code = 200; + $this->response->message = 'ok'; + $this->response->data = array( + 'filter' => array( + 'rules' => $rules + ), + ); + return TRUE; + } + /** * network_address_aliases_get() * diff --git a/pfSense-pkg-FauxAPI/files/etc/inc/fauxapi/fauxapi_pfsense_interface.inc b/pfSense-pkg-FauxAPI/files/etc/inc/fauxapi/fauxapi_pfsense_interface.inc index cef3bd7..7cace1a 100644 --- a/pfSense-pkg-FauxAPI/files/etc/inc/fauxapi/fauxapi_pfsense_interface.inc +++ b/pfSense-pkg-FauxAPI/files/etc/inc/fauxapi/fauxapi_pfsense_interface.inc @@ -32,6 +32,7 @@ include_once '/etc/inc/pkg-utils.inc'; include_once '/usr/local/www/includes/functions.inc.php'; include_once 'fauxapi_pfsense_interface_alias.inc'; +include_once 'fauxapi_pfsense_interface_filter_rules.inc'; class fauxApiPfsenseInterface { public $config_xml_root = 'pfsense'; @@ -766,16 +767,8 @@ class fauxApiPfsenseInterface { return \pfSense_get_interface_stats($interface); } - /** - * filter_rules_get() - * - * @return array - */ - public function filter_rules_get(){ - global $config; - fauxApiLogger::debug(__METHOD__); - return $config["filter"]["rule"]; - } + + use network_filter_rules; use network_address_aliases; diff --git a/pfSense-pkg-FauxAPI/files/etc/inc/fauxapi/fauxapi_pfsense_interface_alias.priv.inc b/pfSense-pkg-FauxAPI/files/etc/inc/fauxapi/fauxapi_pfsense_interface_alias.priv.inc index 9016599..6800b80 100644 --- a/pfSense-pkg-FauxAPI/files/etc/inc/fauxapi/fauxapi_pfsense_interface_alias.priv.inc +++ b/pfSense-pkg-FauxAPI/files/etc/inc/fauxapi/fauxapi_pfsense_interface_alias.priv.inc @@ -2,8 +2,8 @@ namespace fauxapi\v1; // write_config requires functions from this -include '/etc/inc/phpsessionmanager.inc'; -include '/etc/inc/auth.inc'; +include_once '/etc/inc/phpsessionmanager.inc'; +include_once '/etc/inc/auth.inc'; class fauxApiInterfaceAliasTools { diff --git a/pfSense-pkg-FauxAPI/files/etc/inc/fauxapi/fauxapi_pfsense_interface_filter_rules.inc b/pfSense-pkg-FauxAPI/files/etc/inc/fauxapi/fauxapi_pfsense_interface_filter_rules.inc new file mode 100644 index 0000000..f56966a --- /dev/null +++ b/pfSense-pkg-FauxAPI/files/etc/inc/fauxapi/fauxapi_pfsense_interface_filter_rules.inc @@ -0,0 +1,63 @@ + $ruleobj)); + if (isset($ruleobj["id"])) { + $error_message = "rules create obj could not have id"; + $error_data = array('ruleobj' => $ruleobj); + fauxApiLogger::error($error_message, $error_data); + throw new \Exception($error_message); + } + $error_message = fauxApiFiltersRulesTools::CheckRuleObject($ruleobj); + if ($error_message != NULL) { + $error_data = array('ruleobj' => $ruleobj); + fauxApiLogger::error($error_message, $error_data); + throw new \Exception($error_message); + } + $target = fauxApiFiltersRulesTools::BuildRuleConfig($ruleobj); + // insert position + array_splice($config["filter"]["rule"], $position, 0, array($target)); + filter_rules_sort(); + fauxApiFiltersRulesTools::WriteConfig(); + return $config["filter"]["rule"]; + } + + + /** + * filter_rules_delete_by_position() + * + * @return array + */ + public function filter_rules_delete_by_position($position) + { + global $config; + \array_splice($config["filter"]["rule"], $position, 1); + filter_rules_sort(); + fauxApiFiltersRulesTools::WriteConfig(); + return $config["filter"]["rule"]; + } +} diff --git a/pfSense-pkg-FauxAPI/files/etc/inc/fauxapi/fauxapi_pfsense_interface_filter_rules.priv.inc b/pfSense-pkg-FauxAPI/files/etc/inc/fauxapi/fauxapi_pfsense_interface_filter_rules.priv.inc new file mode 100644 index 0000000..a937ee2 --- /dev/null +++ b/pfSense-pkg-FauxAPI/files/etc/inc/fauxapi/fauxapi_pfsense_interface_filter_rules.priv.inc @@ -0,0 +1,776 @@ + $pd) { + if (is_string($pd) && preg_match("/[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f]/", $pd)) { + $input_errors[] = sprintf(gettext("The field %s contains invalid characters."), $pn); + } + } + + if (is_array($reqdfields)) { + for ($i = 0; $i < count($reqdfields); $i++) { + if ($postdata[$reqdfields[$i]] == "") { + $input_errors[] = sprintf(gettext("The field %s is required."), $reqdfieldsn[$i]); + } + } + } + } + + public static function CheckRuleObject($rule) + { + global $config; + global $tcpflags; + $a_filter = &$config['filter']['rule']; + + + $icmplookup = array( + 'inet' => array('name' => 'IPv4', 'icmptypes' => $icmptypes4, 'helpmsg' => gettext('For ICMP rules on IPv4, one or more of these ICMP subtypes may be specified.')), + 'inet6' => array('name' => 'IPv6', 'icmptypes' => $icmptypes6, 'helpmsg' => gettext('For ICMP rules on IPv6, one or more of these ICMP subtypes may be specified.')), + 'inet46' => array('name' => 'IPv4+6', 'icmptypes' => $icmptypes46, 'helpmsg' => sprintf(gettext('For ICMP rules on IPv4+IPv6, one or more of these ICMP subtypes may be specified. (Other ICMP subtypes are only valid under IPv4 %1$sor%2$s IPv6, not both)'), '', '')) + ); + $a_gatewaygroups = return_gateway_groups_array(); + if (!array_key_exists($rule['ipprotocol'], $icmplookup)) { + return gettext("The IP protocol is not recognized."); + } + + // add validation + input error for $rule['interface'] + $valid = ($rule['interface'] == "FloatingRules" || isset($rule['floating'])) ? ['pass', 'block', 'reject', 'match'] : ['pass', 'block', 'reject']; + if (!(is_string($rule['type']) && in_array($rule['type'], $valid))) { + return gettext("A valid rule type is not selected."); + } + + if (isset($rule['tracker']) && !is_numericint($rule['tracker'])) { + return "tracker must be integer"; + } + + if (isset($rule['ipprotocol']) && $rule['gateway'] <> '') { + if (is_array($config['gateways']['gateway_group'])) { + foreach ($config['gateways']['gateway_group'] as $gw_group) { + if ($gw_group['name'] == $rule['gateway'] && $rule['ipprotocol'] != $a_gatewaygroups[$rule['gateway']]['ipprotocol']) { + if ($rule['ipprotocol'] == "inet46") { + return gettext("Gateways can not be assigned in a rule that applies to both IPv4 and IPv6."); + } elseif ($rule['ipprotocol'] == "inet6") { + return gettext("An IPv4 gateway group can not be assigned in IPv6 rules."); + } elseif ($rule['ipprotocol'] == "inet") { + return gettext("An IPv6 gateway group can not be assigned in IPv4 rules."); + } + } + } + } + if ($iptype = is_ipaddr(lookup_gateway_ip_by_name($rule['gateway']))) { + // this also implies that $rule['gateway'] was set and not empty + if ($rule['ipprotocol'] == "inet46") { + return gettext("Gateways can not be assigned in a rule that applies to both IPv4 and IPv6."); + } + if (($rule['ipprotocol'] == "inet6") && ($iptype != 6)) { + return gettext("An IPv4 gateway can not be assigned in IPv6 rules."); + } + if (($rule['ipprotocol'] == "inet") && ($iptype != 4)) { + return gettext("An IPv6 gateway can not be assigned in IPv4 rules."); + } + } + } + + if (($rule['proto'] != "tcp") && ($rule['proto'] != "udp") && ($rule['proto'] != "tcp/udp")) { + $rule['srcbeginport'] = 0; + $rule['srcendport'] = 0; + $rule['dstbeginport'] = 0; + $rule['dstendport'] = 0; + } else { + if ($rule['srcbeginport_cust'] && !$rule['srcbeginport']) { + $rule['srcbeginport'] = trim($rule['srcbeginport_cust']); + } + if ($rule['srcendport_cust'] && !$rule['srcendport']) { + $rule['srcendport'] = trim($rule['srcendport_cust']); + } + if ($rule['srcbeginport'] == "any") { + $rule['srcbeginport'] = 0; + $rule['srcendport'] = 0; + } else { + if (!$rule['srcendport']) { + $rule['srcendport'] = $rule['srcbeginport']; + } + } + if ($rule['srcendport'] == "any") { + $rule['srcendport'] = $rule['srcbeginport']; + } + + if ($rule['dstbeginport_cust'] && !$rule['dstbeginport']) { + $rule['dstbeginport'] = trim($rule['dstbeginport_cust']); + } + if ($rule['dstendport_cust'] && !$rule['dstendport']) { + $rule['dstendport'] = trim($rule['dstendport_cust']); + } + + if ($rule['dstbeginport'] == "any") { + $rule['dstbeginport'] = 0; + $rule['dstendport'] = 0; + } else { + if (!$rule['dstendport']) { + $rule['dstendport'] = $rule['dstbeginport']; + } + } + if ($rule['dstendport'] == "any") { + $rule['dstendport'] = $rule['dstbeginport']; + } + } + + if (fauxApiFiltersRulesTools::is_specialnet($rule['srctype'])) { + $rule['src'] = $rule['srctype']; + $rule['srcmask'] = 0; + } else if ($rule['srctype'] == "single") { + if (is_ipaddrv6($rule['src'])) { + $rule['srcmask'] = 128; + } else { + $rule['srcmask'] = 32; + } + } + if (fauxApiFiltersRulesTools::is_specialnet($rule['dsttype'])) { + $rule['dst'] = $rule['dsttype']; + $rule['dstmask'] = 0; + } else if ($rule['dsttype'] == "single") { + if (is_ipaddrv6($rule['dst'])) { + $rule['dstmask'] = 128; + } else { + $rule['dstmask'] = 32; + } + } + + $pconfig = $rule; + + if (!isset($pconfig['ipprotocol'])) { + // other things depend on this, so ensure a valid value if none provided + $pconfig['ipprotocol'] = "inet"; + } + + if (($rule['proto'] == "icmp") && count($rule['icmptype'])) { + $pconfig['icmptype'] = implode(',', $rule['icmptype']); + } else { + unset($pconfig['icmptype']); + } + + /* input validation */ + $reqdfields = explode(" ", "type"); + $reqdfieldsn = explode(" ", "type"); + + if ($rule['statetype'] == "synproxy state") { + if ($rule['proto'] != "tcp") { + return sprintf(gettext("%s is only valid with protocol TCP."), $rule['statetype']); + } + if ($rule['gateway'] != "") { + return sprintf(gettext("%s is only valid if the gateway is set to 'default'."), $rule['statetype']); + } + } + $not_exists_errs = []; + fauxApiFiltersRulesTools::do_input_validation($rule, $reqdfields, $reqdfieldsn, $not_exists_errs); + if (count($not_exists_errs) > 0) { + return join("\n", $not_exists_errs); + } + + if (!$rule['srcbeginport']) { + $rule['srcbeginport'] = 0; + $rule['srcendport'] = 0; + } + if (!$rule['dstbeginport']) { + $rule['dstbeginport'] = 0; + $rule['dstendport'] = 0; + } + + if ($rule['srcbeginport'] && !is_port_or_alias($rule['srcbeginport'])) { + return sprintf(gettext("%s is not a valid start source port. It must be a port alias or integer between 1 and 65535."), $rule['srcbeginport']); + } + if ($rule['srcendport'] && !is_port_or_alias($rule['srcendport'])) { + return sprintf(gettext("%s is not a valid end source port. It must be a port alias or integer between 1 and 65535."), $rule['srcendport']); + } + if ($rule['dstbeginport'] && !is_port_or_alias($rule['dstbeginport'])) { + return sprintf(gettext("%s is not a valid start destination port. It must be a port alias or integer between 1 and 65535."), $rule['dstbeginport']); + } + if ($rule['dstendport'] && !is_port_or_alias($rule['dstendport'])) { + return sprintf(gettext("%s is not a valid end destination port. It must be a port alias or integer between 1 and 65535."), $rule['dstendport']); + } + if (!$rule['srcbeginport_cust'] && $rule['srcendport_cust']) { + if (is_alias($rule['srcendport_cust'])) { + return 'If a port alias is put in the Source port range to: field the same port alias must be put in the from: field'; + } + } + if ($rule['srcbeginport_cust'] && $rule['srcendport_cust']) { + if (is_alias($rule['srcbeginport_cust']) && is_alias($rule['srcendport_cust']) && $rule['srcbeginport_cust'] != $rule['srcendport_cust']) { + return 'The same port alias must be used in Source port range from: and to: fields'; + } + if ((is_alias($rule['srcbeginport_cust']) && (!is_alias($rule['srcendport_cust']) && $rule['srcendport_cust'] != '')) || + ((!is_alias($rule['srcbeginport_cust']) && $rule['srcbeginport_cust'] != '') && is_alias($rule['srcendport_cust'])) + ) { + return 'Numbers and port aliases cannot be specified at the same time in Source port range from: and to: field'; + } + } + if (!$rule['dstbeginport_cust'] && $rule['dstendport_cust']) { + if (is_alias($rule['dstendport_cust'])) { + return 'If a port alias is put in the Destination port range to: field the same port alias must be put in the from: field'; + } + } + if ($rule['dstbeginport_cust'] && $rule['dstendport_cust']) { + if (is_alias($rule['dstbeginport_cust']) && is_alias($rule['dstendport_cust']) && $rule['dstbeginport_cust'] != $rule['dstendport_cust']) { + return 'The same port alias must be used in Destination port range from: and to: fields'; + } + if ((is_alias($rule['dstbeginport_cust']) && (!is_alias($rule['dstendport_cust']) && $rule['dstendport_cust'] != '')) || + ((!is_alias($rule['dstbeginport_cust']) && $rule['dstbeginport_cust'] != '') && is_alias($rule['dstendport_cust'])) + ) { + return 'Numbers and port aliases cannot be specified at the same time in Destination port range from: and to: field'; + } + } + + if ($rule['src']) { + $rule['src'] = addrtolower(trim($rule['src'])); + } + if ($rule['dst']) { + $rule['dst'] = addrtolower(trim($rule['dst'])); + } + + /* if user enters an alias and selects "network" then disallow. */ + if ($rule['srctype'] == "network") { + if (is_alias($rule['src'])) { + return gettext("Alias entries must be a single host or alias."); + } + } + if ($rule['dsttype'] == "network") { + if (is_alias($rule['dst'])) { + return gettext("Alias entries must be a single host or alias."); + } + } + + if (!fauxApiFiltersRulesTools::is_specialnet($rule['srctype'])) { + if (($rule['src'] && !is_ipaddroralias($rule['src']))) { + return sprintf(gettext("%s is not a valid source IP address or alias."), $rule['src']); + } + if (($rule['srcmask'] && !is_numericint($rule['srcmask']))) { + return gettext("A valid source bit count must be specified."); + } + } + if (!fauxApiFiltersRulesTools::is_specialnet($rule['dsttype'])) { + if (($rule['dst'] && !is_ipaddroralias($rule['dst']))) { + return sprintf(gettext("%s is not a valid destination IP address or alias."), $rule['dst']); + } + if (($rule['dstmask'] && !is_numericint($rule['dstmask']))) { + return gettext("A valid destination bit count must be specified."); + } + } + if ((is_ipaddr($rule['src']) && is_ipaddr($rule['dst']))) { + if (!validate_address_family($rule['src'], $rule['dst'])) { + return gettext("The source and destination IP addresses must have the same family (IPv4 / IPv6)."); + } + } + if ((is_ipaddrv6($rule['src']) || is_ipaddrv6($rule['dst'])) && ($rule['ipprotocol'] == "inet")) { + return gettext("IPv6 addresses cannot be used in IPv4 rules (except within an alias)."); + } + if ((is_ipaddrv4($rule['src']) || is_ipaddrv4($rule['dst'])) && ($rule['ipprotocol'] == "inet6")) { + return gettext("IPv4 addresses can not be used in IPv6 rules (except within an alias)."); + } + + if ((is_ipaddr($rule['src']) || is_ipaddr($rule['dst'])) && ($rule['ipprotocol'] == "inet46")) { + return gettext("IPv4 and IPv6 addresses can not be used in rules that apply to both IPv4 and IPv6 (except within an alias)."); + } + + if ($rule['srcbeginport'] > $rule['srcendport']) { + /* swap */ + $tmp = $rule['srcendport']; + $rule['srcendport'] = $rule['srcbeginport']; + $rule['srcbeginport'] = $tmp; + } + if ($rule['dstbeginport'] > $rule['dstendport']) { + /* swap */ + $tmp = $rule['dstendport']; + $rule['dstendport'] = $rule['dstbeginport']; + $rule['dstbeginport'] = $tmp; + } + if ($rule['os']) { + if ($rule['proto'] != "tcp") { + return gettext("OS detection is only valid with protocol TCP."); + } + if (!in_array($rule['os'], $ostypes)) { + return gettext("Invalid OS detection selection. Please select a valid OS."); + } + } + + if ($rule['proto'] == "icmp") { + $t = $rule['icmptype']; + if (isset($t) && !is_array($t)) { + // shouldn't happen but avoids making assumptions for data-sanitising + return gettext("ICMP types expected to be a list if present, but is not."); + } elseif (!isset($t) || count($t) == 0) { + // not specified or none selected + unset($rule['icmptype']); + } elseif (isset($rule['ipprotocol'])) { + // check data; if ipprotocol invalid then safe to skip this (we can't determine valid icmptypes, but input error already raised for ipprotocol) + $bad_types = array(); + if ((count($t) == 1 && !isset($t['any'])) || count($t) > 1) { + // Only need to check valid if just one selected != "any", or >1 selected + $p = $rule['ipprotocol']; + foreach ($t as $type) { + if (($p == 'inet' && !array_key_exists($type, $icmptypes4)) || + ($p == 'inet6' && !array_key_exists($type, $icmptypes6)) || + ($p == 'inet46' && !array_key_exists($type, $icmptypes46)) + ) { + $bad_types[] = $type; + } + } + } + if (count($bad_types) > 0) { + return sprintf(gettext("Invalid ICMP subtype: %s can not be used with %s."), implode(';', $bad_types), $t['name']); + } + } + } else { + unset($rule['icmptype']); // field not applicable, might hold junk from old hidden selections. Unset it. + } + + if ($rule['ackqueue'] != "") { + if ($rule['defaultqueue'] == "") { + return gettext("A queue must be selected when an acknowledge queue is also selected."); + } else if ($rule['ackqueue'] == $rule['defaultqueue']) { + return gettext("Acknowledge queue and Queue cannot be the same."); + } + } + if (isset($rule['floating']) && $rule['pdnpipe'] != "" && (empty($rule['direction']) || $rule['direction'] == "any")) { + return gettext("Limiters can not be used in Floating rules without choosing a direction."); + } + if (isset($rule['floating']) && $rule['gateway'] != "" && (empty($rule['direction']) || $rule['direction'] == "any")) { + return gettext("Gateways can not be used in Floating rules without choosing a direction."); + } + + /////////////////////////////////////////////////////////////////////////// + // check for sharper + $dnqlist = &get_unique_dnqueue_list(); + if ($rule['pdnpipe'] && $rule['pdnpipe'] != "") { + if ($rule['dnpipe'] == "") { + return gettext("A queue must be selected for the In direction before selecting one for Out too."); + } else if ($rule['pdnpipe'] == $rule['dnpipe']) { + return gettext("In and Out Queue cannot be the same."); + } else if ($dnqlist[$rule['pdnpipe']][0] == "?" && $dnqlist[$rule['dnpipe']][0] <> "?") { + return gettext("A queue and a virtual interface cannot be selected for IN and Out. Both must be from the same type."); + } else if ($dnqlist[$rule['dnpipe']][0] == "?" && $dnqlist[$rule['pdnpipe']][0] <> "?") { + return gettext("A queue and a virtual interface cannot be selected for IN and Out. Both must be from the same type."); + } + if ($rule['direction'] == "out" && empty($rule['gateway'])) { + return gettext("Please select a gateway, normally the interface selected gateway, so the limiters work correctly"); + } + } + if (!empty($rule['ruleid']) && !is_numericint($rule['ruleid'])) { + return gettext('ID must be an integer'); + } + + if (!in_array($rule['proto'], array("tcp", "tcp/udp"))) { + if (!empty($rule['max-src-conn'])) { + return gettext("The maximum number of established connections per host (advanced option) can only be specified for TCP protocol."); + } + if (!empty($rule['max-src-conn-rate']) || !empty($rule['max-src-conn-rates'])) { + return gettext("The maximum new connections per host / per second(s) (advanced option) can only be specified for TCP protocol."); + } + if (!empty($rule['statetimeout'])) { + return gettext("The state timeout (advanced option) can only be specified for TCP protocol."); + } + } + + if ($rule['type'] <> "pass") { + if (!empty($rule['max'])) { + return gettext("The maximum state entries (advanced option) can only be specified for Pass type rules."); + } + if (!empty($rule['max-src-nodes'])) { + return gettext("The maximum number of unique source hosts (advanced option) can only be specified for Pass type rules."); + } + if (!empty($rule['max-src-conn'])) { + return gettext("The maximum number of established connections per host (advanced option) can only be specified for Pass type rules."); + } + if (!empty($rule['max-src-states'])) { + return gettext("The maximum state entries per host (advanced option) can only be specified for Pass type rules."); + } + if (!empty($rule['max-src-conn-rate']) || !empty($rule['max-src-conn-rates'])) { + return gettext("The maximum new connections per host / per second(s) (advanced option) can only be specified for Pass type rules."); + } + if (!empty($rule['statetimeout'])) { + return gettext("The state timeout (advanced option) can only be specified for Pass type rules."); + } + } + + if ($rule['statetype'] == "none") { + if (!empty($rule['max'])) { + return gettext("The maximum state entries (advanced option) cannot be specified if statetype is none."); + } + if (!empty($rule['max-src-nodes'])) { + return gettext("The maximum number of unique source hosts (advanced option) cannot be specified if statetype is none."); + } + if (!empty($rule['max-src-conn'])) { + return gettext("The maximum number of established connections per host (advanced option) cannot be specified if statetype is none."); + } + if (!empty($rule['max-src-states'])) { + return gettext("The maximum state entries per host (advanced option) cannot be specified if statetype is none."); + } + if (!empty($rule['max-src-conn-rate']) || !empty($rule['max-src-conn-rates'])) { + return gettext("The maximum new connections per host / per second(s) (advanced option) cannot be specified if statetype is none."); + } + if (!empty($rule['statetimeout'])) { + return gettext("The state timeout (advanced option) cannot be specified if statetype is none."); + } + } + + if (($rule['max'] != "") && !is_posnumericint($rule['max'])) { + return gettext("Maximum state entries (advanced option) must be a positive integer"); + } + + if (($rule['max-src-nodes'] != "") && !is_posnumericint($rule['max-src-nodes'])) { + return gettext("Maximum number of unique source hosts (advanced option) must be a positive integer"); + } + + if (($rule['max-src-conn'] != "") && !is_posnumericint($rule['max-src-conn'])) { + return gettext("Maximum number of established connections per host (advanced option) must be a positive integer"); + } + + if (($rule['max-src-states'] != "") && !is_posnumericint($rule['max-src-states'])) { + return gettext("Maximum state entries per host (advanced option) must be a positive integer"); + } + + if (($rule['max-src-conn-rate'] != "") && !is_posnumericint($rule['max-src-conn-rate'])) { + return gettext("Maximum new connections per host / per second(s) (advanced option) must be a positive integer"); + } + + if (($rule['statetimeout'] != "") && !is_posnumericint($rule['statetimeout'])) { + return gettext("State timeout (advanced option) must be a positive integer"); + } + + if ((($rule['max-src-conn-rate'] <> "" and $rule['max-src-conn-rates'] == "")) || + (($rule['max-src-conn-rate'] == "" and $rule['max-src-conn-rates'] <> "")) + ) { + return gettext("Both maximum new connections per host and the interval (per second(s)) must be specified"); + } + + if (!$rule['tcpflags_any']) { + $settcpflags = array(); + $outoftcpflags = array(); + foreach ($tcpflags as $tcpflag) { + if ($rule['tcpflags1_' . $tcpflag] == "on") { + $settcpflags[] = $tcpflag; + } + if ($rule['tcpflags2_' . $tcpflag] == "on") { + $outoftcpflags[] = $tcpflag; + } + } + if (empty($outoftcpflags) && !empty($settcpflags)) { + return gettext("If TCP flags that should be set is specified, then out of which flags should be specified as well."); + } + } + + if ($rule['dscp'] && !in_array($rule['dscp'], $firewall_rules_dscp_types)) { + return gettext("Invalid DSCP value."); + } + if ($rule['tag'] && !is_validaliasname($rule['tag'])) { + return gettext("Invalid tag value."); + } + if ($rule['tagged'] && !is_validaliasname($rule['tagged'])) { + return gettext("Invalid tagged value."); + } + if ($rule['statetype'] && !array_key_exists($rule['statetype'], $statetype_values)) { + return gettext("Invalid State Type."); + } + if ($rule['vlanprio'] && !array_key_exists($rule['vlanprio'], $vlanprio)) { + return gettext("Invalid VLAN Prio."); + } + if ($rule['vlanprioset'] && !array_key_exists($rule['vlanprioset'], $vlanprio)) { + return gettext("Invalid VLAN Prio Set."); + } + + if ($rule['ackqueue'] && !array_key_exists($rule['ackqueue'], $list)) { + return gettext("Invalid ACK Queue."); + } + if ($rule['defaultqueue'] && !array_key_exists($rule['defaultqueue'], $list)) { + return gettext("Invalid Default Queue."); + } + + if ($rule['dnpipe'] && !array_key_exists($rule['dnpipe'], $dnqlist)) { + return gettext("Invalid In Pipe."); + } + if ($rule['pdnpipe'] && !array_key_exists($rule['pdnpipe'], $dnqlist)) { + return gettext("Invalid Out Pipe."); + } + return NULL; + } + + public static function BuildRuleConfig($rule) + { + global $tcpflags; + global $config; + $filterent = array(); + $filterent['id'] = $rule['ruleid'] > 0 ? $rule['ruleid'] : ''; + + $filterent['tracker'] = empty($rule['tracker']) ? (int) microtime(true) : $rule['tracker']; + + $filterent['type'] = $rule['type']; + + if (isset($rule['interface'])) { + $filterent['interface'] = $rule['interface']; + } // FIXME: can $rule['interface'] be unset at this point, if so then what? + + $filterent['ipprotocol'] = $rule['ipprotocol']; + + if ($rule['tcpflags_any']) { + $filterent['tcpflags_any'] = true; + } else { + $settcpflags = array(); + $outoftcpflags = array(); + foreach ($tcpflags as $tcpflag) { + if ($rule['tcpflags1_' . $tcpflag] == "on") { + $settcpflags[] = $tcpflag; + } + if ($rule['tcpflags2_' . $tcpflag] == "on") { + $outoftcpflags[] = $tcpflag; + } + } + if (!empty($outoftcpflags)) { + $filterent['tcpflags2'] = join(",", $outoftcpflags); + if (!empty($settcpflags)) { + $filterent['tcpflags1'] = join(",", $settcpflags); + } + } + } + + if (isset($rule['tag'])) { + $filterent['tag'] = $rule['tag']; + } + if (isset($rule['tagged'])) { + $filterent['tagged'] = $rule['tagged']; + } + $if = $rule['interface']; + if ($if == "FloatingRules" || isset($rule['floating'])) { + $filterent['direction'] = $rule['direction']; + if (isset($rule['quick']) && $rule['quick'] <> "") { + $filterent['quick'] = $rule['quick']; + } + $filterent['floating'] = "yes"; + if (isset($rule['interface']) && count($rule['interface']) > 0) { + $filterent['interface'] = implode(",", $rule['interface']); + } + } + + /* Advanced options */ + if ($rule['allowopts'] == "yes") { + $filterent['allowopts'] = true; + } else { + unset($filterent['allowopts']); + } + if ($rule['disablereplyto'] == "yes") { + $filterent['disablereplyto'] = true; + } else { + unset($filterent['disablereplyto']); + } + $filterent['max'] = $rule['max']; + $filterent['max-src-nodes'] = $rule['max-src-nodes']; + $filterent['max-src-conn'] = $rule['max-src-conn']; + $filterent['max-src-states'] = $rule['max-src-states']; + $filterent['statetimeout'] = $rule['statetimeout']; + $filterent['statetype'] = $rule['statetype']; + $filterent['os'] = $rule['os']; + if ($rule['nopfsync'] <> "") { + $filterent['nopfsync'] = true; + } else { + unset($filterent['nopfsync']); + } + + /* Nosync directive - do not xmlrpc sync this item */ + if ($rule['nosync'] <> "") { + $filterent['nosync'] = true; + } else { + unset($filterent['nosync']); + } + + /* unless both values are provided, unset the values - ticket #650 */ + if ($rule['max-src-conn-rate'] <> "" and $rule['max-src-conn-rates'] <> "") { + $filterent['max-src-conn-rate'] = $rule['max-src-conn-rate']; + $filterent['max-src-conn-rates'] = $rule['max-src-conn-rates']; + } else { + unset($filterent['max-src-conn-rate']); + unset($filterent['max-src-conn-rates']); + } + + if ($rule['proto'] != "any") { + $filterent['protocol'] = $rule['proto']; + } else { + unset($filterent['protocol']); + } + + // Convert array of selected ICMP types to comma-separated string, for backwards compatibility (previously only allowed one type per rule) + if ($rule['proto'] == "icmp" && is_array($rule['icmptype']) && !isset($rule['icmptype']['any']) && count($rule['icmptype']) > 0) { + //if any of these conditions not met, rule would apply to all icmptypes, so we would unset + $filterent['icmptype'] = implode(',', $rule['icmptype']); + } else { + unset($filterent['icmptype']); + } + + fauxApiFiltersRulesTools::pconfig_to_address( + $filterent['source'], + $rule['src'], + $rule['srcmask'], + $rule['srcnot'], + $rule['srcbeginport'], + $rule['srcendport'] + ); + + fauxApiFiltersRulesTools::pconfig_to_address( + $filterent['destination'], + $rule['dst'], + $rule['dstmask'], + $rule['dstnot'], + $rule['dstbeginport'], + $rule['dstendport'] + ); + + if ($rule['disabled']) { + $filterent['disabled'] = true; + } else { + unset($filterent['disabled']); + } + + if ($rule['dscp']) { + $filterent['dscp'] = $rule['dscp']; + } + + if ($rule['log']) { + $filterent['log'] = true; + } else { + unset($filterent['log']); + } + + $filterent['descr'] = trim($rule['descr']); + + if ($rule['gateway'] != "") { + $filterent['gateway'] = $rule['gateway']; + } + + if ($rule['defaultqueue'] != "") { + $filterent['defaultqueue'] = $rule['defaultqueue']; + if ($rule['ackqueue'] != "") { + $filterent['ackqueue'] = $rule['ackqueue']; + } + } + + if ($rule['dnpipe'] != "") { + $filterent['dnpipe'] = $rule['dnpipe']; + if ($rule['pdnpipe'] != "") { + $filterent['pdnpipe'] = $rule['pdnpipe']; + } + } + + if ($rule['sched'] != "") { + $filterent['sched'] = $rule['sched']; + } + + if ($rule['vlanprio'] != "") { + $filterent['vlanprio'] = $rule['vlanprio']; + } + if ($rule['vlanprioset'] != "") { + $filterent['vlanprioset'] = $rule['vlanprioset']; + } + + + $a_filter = &$config['filter']['rule']; + // for modify + $id = $rule["id"]; + // If we have an associated nat rule, make sure the source and destination doesn't change + if (isset($a_filter[$id]['associated-rule-id'])) { + $filterent['interface'] = $a_filter[$id]['interface']; + if (isset($a_filter[$id]['protocol'])) { + $filterent['protocol'] = $a_filter[$id]['protocol']; + } else if (isset($filterent['protocol'])) { + unset($filterent['protocol']); + } + if ($a_filter[$id]['protocol'] == "icmp" && $a_filter[$id]['icmptype']) { + $filterent['icmptype'] = $a_filter[$id]['icmptype']; + } else if (isset($filterent['icmptype'])) { + unset($filterent['icmptype']); + } + + $filterent['source'] = $a_filter[$id]['source']; + $filterent['destination'] = $a_filter[$id]['destination']; + $filterent['associated-rule-id'] = $a_filter[$id]['associated-rule-id']; + } + + if (isset($a_filter[$id]['created']) && is_array($a_filter[$id]['created'])) { + $filterent['created'] = $a_filter[$id]['created']; + } else { + $filterent['created'] = make_config_revision_entry(); + } + + $filterent['updated'] = make_config_revision_entry(); + return $filterent; + } + + public static function WriteConfig() + { + if (write_config(gettext("Firewall: Rules - saved/edited a firewall rule."))) { + mark_subsystem_dirty('filter'); + } + } +}