diff --git a/0001-feat-implement-policy-objects-internally.patch b/0001-feat-implement-policy-objects-internally.patch deleted file mode 100644 index 0e5cff535df08c2a2d78c9670c6365570062cd03..0000000000000000000000000000000000000000 --- a/0001-feat-implement-policy-objects-internally.patch +++ /dev/null @@ -1,5993 +0,0 @@ -From 34bdee40aa61f6f973a76233742d41c20d6c6e0b Mon Sep 17 00:00:00 2001 -From: Eric Garver -Date: Fri, 4 Oct 2019 15:23:59 -0400 -Subject: [PATCH] feat: implement policy objects internally - -Introduce policy objects internally. There are no user visible changes. -Rule layout for zones has not changed. Zones are translated into -multiple policies. - -This is the first step in introducing policy objects to users and -therefore supporting output/forward filtering. ---- - src/firewall-config.in | 4 +- - src/firewall/core/base.py | 5 +- - src/firewall/core/ebtables.py | 2 +- - src/firewall/core/fw.py | 16 +- - src/firewall/core/fw_ipset.py | 8 +- - src/firewall/core/fw_policy.py | 1552 +++++++++++++++++++++++++++ - src/firewall/core/fw_transaction.py | 20 +- - src/firewall/core/fw_zone.py | 1880 +++++++-------------------------- - src/firewall/core/io/policy.py | 830 +++++++++++++++ - src/firewall/core/ipXtables.py | 315 +++--- - src/firewall/core/nftables.py | 301 +++--- - src/firewall/errors.py | 1 + - src/firewall/functions.py | 11 +- - src/tests/features/service_include.at | 2 - - 14 files changed, 3118 insertions(+), 1829 deletions(-) - create mode 100644 src/firewall/core/fw_policy.py - create mode 100644 src/firewall/core/io/policy.py - -diff --git a/src/firewall-config.in b/src/firewall-config.in -index f5f69fc..1d88b29 100755 ---- a/src/firewall-config.in -+++ b/src/firewall-config.in -@@ -46,7 +46,7 @@ from firewall import config - from firewall import client - from firewall import functions - from firewall.core.base import DEFAULT_ZONE_TARGET, REJECT_TYPES, \ -- ZONE_SOURCE_IPSET_TYPES -+ SOURCE_IPSET_TYPES - from firewall.core.ipset import IPSET_MAXNAMELEN - from firewall.core.helper import HELPER_MAXNAMELEN - from firewall.core.io.zone import Zone -@@ -4188,7 +4188,7 @@ class FirewallConfig(object): - continue - raise - self.activate_exception_handler() -- if settings.getType() not in ZONE_SOURCE_IPSET_TYPES: -+ if settings.getType() not in SOURCE_IPSET_TYPES: - continue - ipsets[x] = settings - else: -diff --git a/src/firewall/core/base.py b/src/firewall/core/base.py -index e2691b5..45522a9 100644 ---- a/src/firewall/core/base.py -+++ b/src/firewall/core/base.py -@@ -22,10 +22,13 @@ - """Base firewall settings""" - - DEFAULT_ZONE_TARGET = "{chain}_{zone}" -+DEFAULT_POLICY_TARGET = "CONTINUE" - - ZONE_TARGETS = [ "ACCEPT", "%%REJECT%%", "DROP", DEFAULT_ZONE_TARGET, - "default" ] - -+POLICY_TARGETS = [ "ACCEPT", "%%REJECT%%", "DROP", "CONTINUE" ] -+ - SHORTCUTS = { - "PREROUTING": "PRE", - "POSTROUTING": "POST", -@@ -49,7 +52,7 @@ REJECT_TYPES = { - # ipset types that can be used as a source in zones - # The match-set option will be src or src,src according to the - # dimension of the ipset. --ZONE_SOURCE_IPSET_TYPES = [ -+SOURCE_IPSET_TYPES = [ - "hash:ip", "hash:ip,port", "hash:ip,mark", - "hash:net", "hash:net,port", "hash:net,iface", - "hash:mac" -diff --git a/src/firewall/core/ebtables.py b/src/firewall/core/ebtables.py -index a5860bd..8938b75 100644 ---- a/src/firewall/core/ebtables.py -+++ b/src/firewall/core/ebtables.py -@@ -52,7 +52,7 @@ for table in BUILT_IN_CHAINS.keys(): - class ebtables(object): - ipv = "eb" - name = "ebtables" -- zones_supported = False # ebtables only supported with direct interface -+ policies_supported = False # ebtables only supported with direct interface - - def __init__(self): - self._command = COMMANDS[self.ipv] -diff --git a/src/firewall/core/fw.py b/src/firewall/core/fw.py -index f3f1adf..a9d56a9 100644 ---- a/src/firewall/core/fw.py -+++ b/src/firewall/core/fw.py -@@ -42,6 +42,7 @@ from firewall.core.fw_policies import FirewallPolicies - from firewall.core.fw_ipset import FirewallIPSet - from firewall.core.fw_transaction import FirewallTransaction - from firewall.core.fw_helper import FirewallHelper -+from firewall.core.fw_policy import FirewallPolicy - from firewall.core.fw_nm import nm_get_bus_name, nm_get_interfaces_in_zone - from firewall.core.logger import log - from firewall.core.io.firewalld_conf import firewalld_conf -@@ -98,6 +99,7 @@ class Firewall(object): - self.policies = FirewallPolicies() - self.ipset = FirewallIPSet(self) - self.helper = FirewallHelper(self) -+ self.policy = FirewallPolicy(self) - - self.__init_vars() - -@@ -634,6 +636,7 @@ class Firewall(object): - self.config.cleanup() - self.direct.cleanup() - self.policies.cleanup() -+ self.policy.cleanup() - self._firewalld_conf.cleanup() - self.__init_vars() - -@@ -876,6 +879,12 @@ class Firewall(object): - if self._panic: - raise FirewallError(errors.PANIC_MODE) - -+ def check_policy(self, policy): -+ _policy = policy -+ if _policy not in self.policy.get_policies(): -+ raise FirewallError(errors.INVALID_POLICY, _policy) -+ return _policy -+ - def check_zone(self, zone): - _zone = zone - if not _zone or _zone == "": -@@ -996,8 +1005,11 @@ class Firewall(object): - # add interfaces to zones again - for zone in self.zone.get_zones(): - if zone in _zone_interfaces: -- self.zone.set_settings(zone, { "interfaces": -- _zone_interfaces[zone] }) -+ -+ for interface_id in _zone_interfaces[zone]: -+ self.zone.change_zone_of_interface(zone, interface_id, -+ _zone_interfaces[zone][interface_id]["sender"]) -+ - del _zone_interfaces[zone] - else: - log.info1("New zone '%s'.", zone) -diff --git a/src/firewall/core/fw_ipset.py b/src/firewall/core/fw_ipset.py -index 6136b47..102c7f6 100644 ---- a/src/firewall/core/fw_ipset.py -+++ b/src/firewall/core/fw_ipset.py -@@ -155,8 +155,8 @@ class FirewallIPSet(object): - - # TYPE - -- def get_type(self, name): -- return self.get_ipset(name, applied=True).type -+ def get_type(self, name, applied=True): -+ return self.get_ipset(name, applied=applied).type - - # DIMENSION - def get_dimension(self, name): -@@ -173,8 +173,8 @@ class FirewallIPSet(object): - - # OPTIONS - -- def get_family(self, name): -- obj = self.get_ipset(name, applied=True) -+ def get_family(self, name, applied=True): -+ obj = self.get_ipset(name, applied=applied) - if "family" in obj.options: - if obj.options["family"] == "inet6": - return "ipv6" -diff --git a/src/firewall/core/fw_policy.py b/src/firewall/core/fw_policy.py -new file mode 100644 -index 0000000..d22e126 ---- /dev/null -+++ b/src/firewall/core/fw_policy.py -@@ -0,0 +1,1552 @@ -+# -*- coding: utf-8 -*- -+# -+# SPDX-License-Identifier: GPL-2.0-or-later -+ -+import time -+from firewall.core.logger import log -+from firewall.functions import portStr, checkIPnMask, checkIP6nMask, \ -+ checkProtocol, enable_ip_forwarding, check_single_address, \ -+ portInPortRange, get_nf_conntrack_short_name, coalescePortRange, breakPortRange -+from firewall.core.rich import Rich_Rule, Rich_Accept, \ -+ Rich_Mark, Rich_Service, Rich_Port, Rich_Protocol, \ -+ Rich_Masquerade, Rich_ForwardPort, Rich_SourcePort, Rich_IcmpBlock, \ -+ Rich_IcmpType -+from firewall.core.fw_transaction import FirewallTransaction -+from firewall import errors -+from firewall.errors import FirewallError -+from firewall.fw_types import LastUpdatedOrderedDict -+from firewall.core.base import SOURCE_IPSET_TYPES -+ -+class FirewallPolicy(object): -+ def __init__(self, fw): -+ self._fw = fw -+ self._chains = { } -+ self._policies = { } -+ -+ def __repr__(self): -+ return '%s(%r, %r)' % (self.__class__, self._chains, self._policies) -+ -+ def cleanup(self): -+ self._chains.clear() -+ self._policies.clear() -+ -+ # transaction -+ -+ def new_transaction(self): -+ return FirewallTransaction(self._fw) -+ -+ # policies -+ -+ def get_policies(self): -+ return sorted(self._policies.keys()) -+ -+ def get_policy(self, policy): -+ p = self._fw.check_policy(policy) -+ return self._policies[p] -+ -+ def _first_except(self, e, f, name, *args, **kwargs): -+ try: -+ f(name, *args, **kwargs) -+ except FirewallError as error: -+ if not e: -+ return error -+ return e -+ -+ def add_policy(self, obj): -+ obj.settings = { x : LastUpdatedOrderedDict() -+ for x in [ "services", "ports", -+ "masquerade", "forward_ports", -+ "source_ports", -+ "icmp_blocks", "rules", -+ "protocols", "icmp_block_inversion" ] } -+ -+ self._policies[obj.name] = obj -+ self.copy_permanent_to_runtime(obj.name) -+ -+ def remove_policy(self, policy): -+ obj = self._policies[policy] -+ if obj.applied: -+ self.unapply_policy_settings(policy) -+ obj.settings.clear() -+ del self._policies[policy] -+ -+ def copy_permanent_to_runtime(self, policy): -+ obj = self._policies[policy] -+ -+ if obj.applied: -+ return -+ -+ for args in obj.icmp_blocks: -+ self.add_icmp_block(policy, args) -+ for args in obj.forward_ports: -+ self.add_forward_port(policy, *args) -+ for args in obj.services: -+ self.add_service(policy, args) -+ for args in obj.ports: -+ self.add_port(policy, *args) -+ for args in obj.protocols: -+ self.add_protocol(policy, args) -+ for args in obj.source_ports: -+ self.add_source_port(policy, *args) -+ for args in obj.rules: -+ self.add_rule(policy, args) -+ if obj.masquerade: -+ self.add_masquerade(policy) -+ -+ def set_policy_applied(self, policy, applied): -+ obj = self._policies[policy] -+ obj.applied = applied -+ -+ # settings -+ -+ # generate settings record with sender, timeout -+ def __gen_settings(self, timeout, sender): -+ ret = { -+ "date": time.time(), -+ "sender": sender, -+ "timeout": timeout, -+ } -+ return ret -+ -+ def get_settings(self, policy): -+ return self.get_policy(policy).settings -+ -+ def _policy_settings(self, enable, policy, use_transaction=None): -+ _policy = self._fw.check_policy(policy) -+ obj = self._policies[_policy] -+ if (enable and obj.applied) or (not enable and not obj.applied): -+ return -+ if enable: -+ obj.applied = True -+ -+ if use_transaction is None: -+ transaction = self.new_transaction() -+ else: -+ transaction = use_transaction -+ -+ settings = self.get_settings(policy) -+ for key in settings: -+ for args in settings[key]: -+ if key == "icmp_blocks": -+ self._icmp_block(enable, _policy, args, transaction) -+ elif key == "icmp_block_inversion": -+ continue -+ elif key == "forward_ports": -+ self._forward_port(enable, _policy, transaction, -+ *args) -+ elif key == "services": -+ self._service(enable, _policy, args, transaction) -+ elif key == "ports": -+ self._port(enable, _policy, args[0], args[1], -+ transaction) -+ elif key == "protocols": -+ self._protocol(enable, _policy, args, transaction) -+ elif key == "source_ports": -+ self._source_port(enable, _policy, args[0], args[1], -+ transaction) -+ elif key == "masquerade": -+ self._masquerade(enable, _policy, transaction) -+ elif key == "rules": -+ self.__rule(enable, _policy, Rich_Rule(rule_str=args), -+ transaction) -+ else: -+ log.warning("Policy '%s': Unknown setting '%s:%s', " -+ "unable to apply", policy, key, args) -+ -+ if use_transaction is None: -+ transaction.execute(enable) -+ -+ def apply_policy_settings(self, policy, use_transaction=None): -+ self._policy_settings(True, policy, use_transaction=use_transaction) -+ -+ def unapply_policy_settings(self, policy, use_transaction=None): -+ self._policy_settings(False, policy, use_transaction=use_transaction) -+ -+ def get_config_with_settings(self, policy): -+ """ -+ :return: exported config updated with runtime settings -+ """ -+ conf = list(self.get_policy(policy).export_config()) -+ conf[5] = self.list_services(policy) -+ conf[6] = self.list_ports(policy) -+ conf[7] = self.list_icmp_blocks(policy) -+ conf[8] = self.query_masquerade(policy) -+ conf[9] = self.list_forward_ports(policy) -+ conf[12] = self.list_rules(policy) -+ conf[13] = self.list_protocols(policy) -+ conf[14] = self.list_source_ports(policy) -+ conf[15] = self.query_icmp_block_inversion(policy) -+ return tuple(conf) -+ -+ # RICH LANGUAGE -+ -+ def check_rule(self, rule): -+ rule.check() -+ -+ def __rule_id(self, rule): -+ self.check_rule(rule) -+ return str(rule) -+ -+ def _rule_source_ipv(self, source): -+ if not source: -+ return None -+ -+ if source.addr: -+ if checkIPnMask(source.addr): -+ return "ipv4" -+ elif checkIP6nMask(source.addr): -+ return "ipv6" -+ elif hasattr(source, "mac") and source.mac: -+ return "" -+ elif hasattr(source, "ipset") and source.ipset: -+ self._check_ipset_type_for_source(source.ipset) -+ self._check_ipset_applied(source.ipset) -+ return self._ipset_family(source.ipset) -+ -+ return None -+ -+ def __rule(self, enable, policy, rule, transaction): -+ self._rule_prepare(enable, policy, rule, transaction) -+ -+ def add_rule(self, policy, rule, timeout=0, sender=None, -+ use_transaction=None): -+ _policy = self._fw.check_policy(policy) -+ self._fw.check_timeout(timeout) -+ self._fw.check_panic() -+ _obj = self._policies[_policy] -+ -+ rule_id = self.__rule_id(rule) -+ if rule_id in _obj.settings["rules"]: -+ _name = _obj.derived_from_zone if _obj.derived_from_zone else _policy -+ raise FirewallError(errors.ALREADY_ENABLED, -+ "'%s' already in '%s'" % (rule, _name)) -+ -+ if use_transaction is None: -+ transaction = self.new_transaction() -+ else: -+ transaction = use_transaction -+ -+ if _obj.applied: -+ self.__rule(True, _policy, rule, transaction) -+ -+ self.__register_rule(_obj, rule_id, timeout, sender) -+ transaction.add_fail(self.__unregister_rule, _obj, rule_id) -+ -+ if use_transaction is None: -+ transaction.execute(True) -+ -+ return _policy -+ -+ def __register_rule(self, _obj, rule_id, timeout, sender): -+ _obj.settings["rules"][rule_id] = self.__gen_settings( -+ timeout, sender) -+ -+ def remove_rule(self, policy, rule, -+ use_transaction=None): -+ _policy = self._fw.check_policy(policy) -+ self._fw.check_panic() -+ _obj = self._policies[_policy] -+ -+ rule_id = self.__rule_id(rule) -+ if rule_id not in _obj.settings["rules"]: -+ _name = _obj.derived_from_zone if _obj.derived_from_zone else _policy -+ raise FirewallError(errors.NOT_ENABLED, -+ "'%s' not in '%s'" % (rule, _name)) -+ -+ if use_transaction is None: -+ transaction = self.new_transaction() -+ else: -+ transaction = use_transaction -+ -+ if _obj.applied: -+ self.__rule(False, _policy, rule, transaction) -+ -+ transaction.add_post(self.__unregister_rule, _obj, rule_id) -+ -+ if use_transaction is None: -+ transaction.execute(True) -+ -+ return _policy -+ -+ def __unregister_rule(self, _obj, rule_id): -+ if rule_id in _obj.settings["rules"]: -+ del _obj.settings["rules"][rule_id] -+ -+ def query_rule(self, policy, rule): -+ return self.__rule_id(rule) in self.get_settings(policy)["rules"] -+ -+ def list_rules(self, policy): -+ return list(self.get_settings(policy)["rules"].keys()) -+ -+ # SERVICES -+ -+ def check_service(self, service): -+ self._fw.check_service(service) -+ -+ def __service_id(self, service): -+ self.check_service(service) -+ return service -+ -+ def add_service(self, policy, service, timeout=0, sender=None, -+ use_transaction=None): -+ _policy = self._fw.check_policy(policy) -+ self._fw.check_timeout(timeout) -+ self._fw.check_panic() -+ _obj = self._policies[_policy] -+ -+ service_id = self.__service_id(service) -+ if service_id in _obj.settings["services"]: -+ _name = _obj.derived_from_zone if _obj.derived_from_zone else _policy -+ raise FirewallError(errors.ALREADY_ENABLED, -+ "'%s' already in '%s'" % (service, _name)) -+ -+ if use_transaction is None: -+ transaction = self.new_transaction() -+ else: -+ transaction = use_transaction -+ -+ if _obj.applied: -+ self._service(True, _policy, service, transaction) -+ -+ self.__register_service(_obj, service_id, timeout, sender) -+ transaction.add_fail(self.__unregister_service, _obj, service_id) -+ -+ if use_transaction is None: -+ transaction.execute(True) -+ -+ return _policy -+ -+ def __register_service(self, _obj, service_id, timeout, sender): -+ _obj.settings["services"][service_id] = \ -+ self.__gen_settings(timeout, sender) -+ -+ def remove_service(self, policy, service, -+ use_transaction=None): -+ _policy = self._fw.check_policy(policy) -+ self._fw.check_panic() -+ _obj = self._policies[_policy] -+ -+ service_id = self.__service_id(service) -+ if service_id not in _obj.settings["services"]: -+ _name = _obj.derived_from_zone if _obj.derived_from_zone else _policy -+ raise FirewallError(errors.NOT_ENABLED, -+ "'%s' not in '%s'" % (service, _name)) -+ -+ if use_transaction is None: -+ transaction = self.new_transaction() -+ else: -+ transaction = use_transaction -+ -+ if _obj.applied: -+ self._service(False, _policy, service, transaction) -+ -+ transaction.add_post(self.__unregister_service, _obj, service_id) -+ -+ if use_transaction is None: -+ transaction.execute(True) -+ -+ return _policy -+ -+ def __unregister_service(self, _obj, service_id): -+ if service_id in _obj.settings["services"]: -+ del _obj.settings["services"][service_id] -+ -+ def query_service(self, policy, service): -+ return self.__service_id(service) in self.get_settings(policy)["services"] -+ -+ def list_services(self, policy): -+ return self.get_settings(policy)["services"].keys() -+ -+ def get_helpers_for_service_helpers(self, helpers): -+ _helpers = [ ] -+ for helper in helpers: -+ try: -+ _helper = self._fw.helper.get_helper(helper) -+ except FirewallError: -+ raise FirewallError(errors.INVALID_HELPER, helper) -+ _helpers.append(_helper) -+ return _helpers -+ -+ def get_helpers_for_service_modules(self, modules, enable): -+ # If automatic helper assignment is turned off, helpers that -+ # do not have ports defined will be replaced by the helpers -+ # that the helper.module defines. -+ _helpers = [ ] -+ for module in modules: -+ try: -+ helper = self._fw.helper.get_helper(module) -+ except FirewallError: -+ raise FirewallError(errors.INVALID_HELPER, module) -+ if len(helper.ports) < 1: -+ _module_short_name = get_nf_conntrack_short_name(helper.module) -+ try: -+ _helper = self._fw.helper.get_helper(_module_short_name) -+ _helpers.append(_helper) -+ except FirewallError: -+ if enable: -+ log.warning("Helper '%s' is not available" % _module_short_name) -+ continue -+ else: -+ _helpers.append(helper) -+ return _helpers -+ -+ # PORTS -+ -+ def check_port(self, port, protocol): -+ self._fw.check_port(port) -+ self._fw.check_tcpudp(protocol) -+ -+ def __port_id(self, port, protocol): -+ self.check_port(port, protocol) -+ return (portStr(port, "-"), protocol) -+ -+ def add_port(self, policy, port, protocol, timeout=0, sender=None, -+ use_transaction=None): -+ _policy = self._fw.check_policy(policy) -+ self._fw.check_timeout(timeout) -+ self._fw.check_panic() -+ _obj = self._policies[_policy] -+ -+ existing_port_ids = list(filter(lambda x: x[1] == protocol, _obj.settings["ports"])) -+ for port_id in existing_port_ids: -+ if portInPortRange(port, port_id[0]): -+ _name = _obj.derived_from_zone if _obj.derived_from_zone else _policy -+ raise FirewallError(errors.ALREADY_ENABLED, -+ "'%s:%s' already in '%s'" % (port, protocol, _name)) -+ -+ added_ranges, removed_ranges = coalescePortRange(port, [_port for (_port, _protocol) in existing_port_ids]) -+ -+ if use_transaction is None: -+ transaction = self.new_transaction() -+ else: -+ transaction = use_transaction -+ -+ if _obj.applied: -+ for range in added_ranges: -+ self._port(True, _policy, portStr(range, "-"), protocol, transaction) -+ for range in removed_ranges: -+ self._port(False, _policy, portStr(range, "-"), protocol, transaction) -+ -+ for range in added_ranges: -+ port_id = self.__port_id(range, protocol) -+ self.__register_port(_obj, port_id, timeout, sender) -+ transaction.add_fail(self.__unregister_port, _obj, port_id) -+ for range in removed_ranges: -+ port_id = self.__port_id(range, protocol) -+ transaction.add_post(self.__unregister_port, _obj, port_id) -+ -+ if use_transaction is None: -+ transaction.execute(True) -+ -+ return _policy -+ -+ def __register_port(self, _obj, port_id, timeout, sender): -+ _obj.settings["ports"][port_id] = \ -+ self.__gen_settings(timeout, sender) -+ -+ def remove_port(self, policy, port, protocol, -+ use_transaction=None): -+ _policy = self._fw.check_policy(policy) -+ self._fw.check_panic() -+ _obj = self._policies[_policy] -+ -+ existing_port_ids = list(filter(lambda x: x[1] == protocol, _obj.settings["ports"])) -+ for port_id in existing_port_ids: -+ if portInPortRange(port, port_id[0]): -+ break -+ else: -+ _name = _obj.derived_from_zone if _obj.derived_from_zone else _policy -+ raise FirewallError(errors.NOT_ENABLED, -+ "'%s:%s' not in '%s'" % (port, protocol, _name)) -+ -+ added_ranges, removed_ranges = breakPortRange(port, [_port for (_port, _protocol) in existing_port_ids]) -+ -+ if use_transaction is None: -+ transaction = self.new_transaction() -+ else: -+ transaction = use_transaction -+ -+ if _obj.applied: -+ for range in added_ranges: -+ self._port(True, _policy, portStr(range, "-"), protocol, transaction) -+ for range in removed_ranges: -+ self._port(False, _policy, portStr(range, "-"), protocol, transaction) -+ -+ for range in added_ranges: -+ port_id = self.__port_id(range, protocol) -+ self.__register_port(_obj, port_id, 0, None) -+ transaction.add_fail(self.__unregister_port, _obj, port_id) -+ for range in removed_ranges: -+ port_id = self.__port_id(range, protocol) -+ transaction.add_post(self.__unregister_port, _obj, port_id) -+ -+ if use_transaction is None: -+ transaction.execute(True) -+ -+ return _policy -+ -+ def __unregister_port(self, _obj, port_id): -+ if port_id in _obj.settings["ports"]: -+ del _obj.settings["ports"][port_id] -+ -+ def query_port(self, policy, port, protocol): -+ for (_port, _protocol) in self.get_settings(policy)["ports"]: -+ if portInPortRange(port, _port) and protocol == _protocol: -+ return True -+ -+ return False -+ -+ def list_ports(self, policy): -+ return list(self.get_settings(policy)["ports"].keys()) -+ -+ # PROTOCOLS -+ -+ def check_protocol(self, protocol): -+ if not checkProtocol(protocol): -+ raise FirewallError(errors.INVALID_PROTOCOL, protocol) -+ -+ def __protocol_id(self, protocol): -+ self.check_protocol(protocol) -+ return protocol -+ -+ def add_protocol(self, policy, protocol, timeout=0, sender=None, -+ use_transaction=None): -+ _policy = self._fw.check_policy(policy) -+ self._fw.check_timeout(timeout) -+ self._fw.check_panic() -+ _obj = self._policies[_policy] -+ -+ protocol_id = self.__protocol_id(protocol) -+ if protocol_id in _obj.settings["protocols"]: -+ _name = _obj.derived_from_zone if _obj.derived_from_zone else _policy -+ raise FirewallError(errors.ALREADY_ENABLED, -+ "'%s' already in '%s'" % (protocol, _name)) -+ -+ if use_transaction is None: -+ transaction = self.new_transaction() -+ else: -+ transaction = use_transaction -+ -+ if _obj.applied: -+ self._protocol(True, _policy, protocol, transaction) -+ -+ self.__register_protocol(_obj, protocol_id, timeout, sender) -+ transaction.add_fail(self.__unregister_protocol, _obj, protocol_id) -+ -+ if use_transaction is None: -+ transaction.execute(True) -+ -+ return _policy -+ -+ def __register_protocol(self, _obj, protocol_id, timeout, sender): -+ _obj.settings["protocols"][protocol_id] = \ -+ self.__gen_settings(timeout, sender) -+ -+ def remove_protocol(self, policy, protocol, -+ use_transaction=None): -+ _policy = self._fw.check_policy(policy) -+ self._fw.check_panic() -+ _obj = self._policies[_policy] -+ -+ protocol_id = self.__protocol_id(protocol) -+ if protocol_id not in _obj.settings["protocols"]: -+ _name = _obj.derived_from_zone if _obj.derived_from_zone else _policy -+ raise FirewallError(errors.NOT_ENABLED, -+ "'%s' not in '%s'" % (protocol, _name)) -+ -+ if use_transaction is None: -+ transaction = self.new_transaction() -+ else: -+ transaction = use_transaction -+ -+ if _obj.applied: -+ self._protocol(False, _policy, protocol, transaction) -+ -+ transaction.add_post(self.__unregister_protocol, _obj, -+ protocol_id) -+ -+ if use_transaction is None: -+ transaction.execute(True) -+ -+ return _policy -+ -+ def __unregister_protocol(self, _obj, protocol_id): -+ if protocol_id in _obj.settings["protocols"]: -+ del _obj.settings["protocols"][protocol_id] -+ -+ def query_protocol(self, policy, protocol): -+ return self.__protocol_id(protocol) in self.get_settings(policy)["protocols"] -+ -+ def list_protocols(self, policy): -+ return list(self.get_settings(policy)["protocols"].keys()) -+ -+ # SOURCE PORTS -+ -+ def __source_port_id(self, port, protocol): -+ self.check_port(port, protocol) -+ return (portStr(port, "-"), protocol) -+ -+ def add_source_port(self, policy, port, protocol, timeout=0, sender=None, -+ use_transaction=None): -+ _policy = self._fw.check_policy(policy) -+ self._fw.check_timeout(timeout) -+ self._fw.check_panic() -+ _obj = self._policies[_policy] -+ -+ existing_port_ids = list(filter(lambda x: x[1] == protocol, _obj.settings["source_ports"])) -+ for port_id in existing_port_ids: -+ if portInPortRange(port, port_id[0]): -+ _name = _obj.derived_from_zone if _obj.derived_from_zone else _policy -+ raise FirewallError(errors.ALREADY_ENABLED, -+ "'%s:%s' already in '%s'" % (port, protocol, _name)) -+ -+ added_ranges, removed_ranges = coalescePortRange(port, [_port for (_port, _protocol) in existing_port_ids]) -+ -+ if use_transaction is None: -+ transaction = self.new_transaction() -+ else: -+ transaction = use_transaction -+ -+ if _obj.applied: -+ for range in added_ranges: -+ self._source_port(True, _policy, portStr(range, "-"), protocol, transaction) -+ for range in removed_ranges: -+ self._source_port(False, _policy, portStr(range, "-"), protocol, transaction) -+ -+ for range in added_ranges: -+ port_id = self.__source_port_id(range, protocol) -+ self.__register_source_port(_obj, port_id, timeout, sender) -+ transaction.add_fail(self.__unregister_source_port, _obj, port_id) -+ for range in removed_ranges: -+ port_id = self.__source_port_id(range, protocol) -+ transaction.add_post(self.__unregister_source_port, _obj, port_id) -+ -+ if use_transaction is None: -+ transaction.execute(True) -+ -+ return _policy -+ -+ def __register_source_port(self, _obj, port_id, timeout, sender): -+ _obj.settings["source_ports"][port_id] = \ -+ self.__gen_settings(timeout, sender) -+ -+ def remove_source_port(self, policy, port, protocol, -+ use_transaction=None): -+ _policy = self._fw.check_policy(policy) -+ self._fw.check_panic() -+ _obj = self._policies[_policy] -+ -+ existing_port_ids = list(filter(lambda x: x[1] == protocol, _obj.settings["source_ports"])) -+ for port_id in existing_port_ids: -+ if portInPortRange(port, port_id[0]): -+ break -+ else: -+ _name = _obj.derived_from_zone if _obj.derived_from_zone else _policy -+ raise FirewallError(errors.NOT_ENABLED, -+ "'%s:%s' not in '%s'" % (port, protocol, _name)) -+ -+ added_ranges, removed_ranges = breakPortRange(port, [_port for (_port, _protocol) in existing_port_ids]) -+ -+ if use_transaction is None: -+ transaction = self.new_transaction() -+ else: -+ transaction = use_transaction -+ -+ if _obj.applied: -+ for range in added_ranges: -+ self._source_port(True, _policy, portStr(range, "-"), protocol, transaction) -+ for range in removed_ranges: -+ self._source_port(False, _policy, portStr(range, "-"), protocol, transaction) -+ -+ for range in added_ranges: -+ port_id = self.__source_port_id(range, protocol) -+ self.__register_source_port(_obj, port_id, 0, None) -+ transaction.add_fail(self.__unregister_source_port, _obj, port_id) -+ for range in removed_ranges: -+ port_id = self.__source_port_id(range, protocol) -+ transaction.add_post(self.__unregister_source_port, _obj, port_id) -+ -+ if use_transaction is None: -+ transaction.execute(True) -+ -+ return _policy -+ -+ def __unregister_source_port(self, _obj, port_id): -+ if port_id in _obj.settings["source_ports"]: -+ del _obj.settings["source_ports"][port_id] -+ -+ def query_source_port(self, policy, port, protocol): -+ for (_port, _protocol) in self.get_settings(policy)["source_ports"]: -+ if portInPortRange(port, _port) and protocol == _protocol: -+ return True -+ -+ return False -+ -+ def list_source_ports(self, policy): -+ return list(self.get_settings(policy)["source_ports"].keys()) -+ -+ # MASQUERADE -+ -+ def __masquerade_id(self): -+ return True -+ -+ def add_masquerade(self, policy, timeout=0, sender=None, -+ use_transaction=None): -+ _policy = self._fw.check_policy(policy) -+ self._fw.check_timeout(timeout) -+ self._fw.check_panic() -+ _obj = self._policies[_policy] -+ -+ masquerade_id = self.__masquerade_id() -+ if masquerade_id in _obj.settings["masquerade"]: -+ _name = _obj.derived_from_zone if _obj.derived_from_zone else _policy -+ raise FirewallError(errors.ALREADY_ENABLED, -+ "masquerade already enabled in '%s'" % _name) -+ -+ if use_transaction is None: -+ transaction = self.new_transaction() -+ else: -+ transaction = use_transaction -+ -+ if _obj.applied: -+ self._masquerade(True, _policy, transaction) -+ -+ self.__register_masquerade(_obj, masquerade_id, timeout, sender) -+ transaction.add_fail(self.__unregister_masquerade, _obj, masquerade_id) -+ -+ if use_transaction is None: -+ transaction.execute(True) -+ -+ return _policy -+ -+ def __register_masquerade(self, _obj, masquerade_id, timeout, sender): -+ _obj.settings["masquerade"][masquerade_id] = \ -+ self.__gen_settings(timeout, sender) -+ -+ def remove_masquerade(self, policy, use_transaction=None): -+ _policy = self._fw.check_policy(policy) -+ self._fw.check_panic() -+ _obj = self._policies[_policy] -+ -+ masquerade_id = self.__masquerade_id() -+ if masquerade_id not in _obj.settings["masquerade"]: -+ _name = _obj.derived_from_zone if _obj.derived_from_zone else _policy -+ raise FirewallError(errors.NOT_ENABLED, -+ "masquerade not enabled in '%s'" % _name) -+ -+ if use_transaction is None: -+ transaction = self.new_transaction() -+ else: -+ transaction = use_transaction -+ -+ if _obj.applied: -+ self._masquerade(False, _policy, transaction) -+ -+ transaction.add_post(self.__unregister_masquerade, _obj, masquerade_id) -+ -+ if use_transaction is None: -+ transaction.execute(True) -+ -+ return _policy -+ -+ def __unregister_masquerade(self, _obj, masquerade_id): -+ if masquerade_id in _obj.settings["masquerade"]: -+ del _obj.settings["masquerade"][masquerade_id] -+ -+ def query_masquerade(self, policy): -+ return self.__masquerade_id() in self.get_settings(policy)["masquerade"] -+ -+ # PORT FORWARDING -+ -+ def check_forward_port(self, ipv, port, protocol, toport=None, toaddr=None): -+ self._fw.check_port(port) -+ self._fw.check_tcpudp(protocol) -+ if toport: -+ self._fw.check_port(toport) -+ if toaddr: -+ if not check_single_address(ipv, toaddr): -+ raise FirewallError(errors.INVALID_ADDR, toaddr) -+ if not toport and not toaddr: -+ raise FirewallError( -+ errors.INVALID_FORWARD, -+ "port-forwarding is missing to-port AND to-addr") -+ -+ def __forward_port_id(self, port, protocol, toport=None, toaddr=None): -+ if check_single_address("ipv6", toaddr): -+ self.check_forward_port("ipv6", port, protocol, toport, toaddr) -+ else: -+ self.check_forward_port("ipv4", port, protocol, toport, toaddr) -+ return (portStr(port, "-"), protocol, -+ portStr(toport, "-"), str(toaddr)) -+ -+ def add_forward_port(self, policy, port, protocol, toport=None, -+ toaddr=None, timeout=0, sender=None, -+ use_transaction=None): -+ _policy = self._fw.check_policy(policy) -+ self._fw.check_timeout(timeout) -+ self._fw.check_panic() -+ _obj = self._policies[_policy] -+ -+ forward_id = self.__forward_port_id(port, protocol, toport, toaddr) -+ if forward_id in _obj.settings["forward_ports"]: -+ _name = _obj.derived_from_zone if _obj.derived_from_zone else _policy -+ raise FirewallError(errors.ALREADY_ENABLED, -+ "'%s:%s:%s:%s' already in '%s'" % \ -+ (port, protocol, toport, toaddr, _name)) -+ -+ if use_transaction is None: -+ transaction = self.new_transaction() -+ else: -+ transaction = use_transaction -+ -+ if _obj.applied: -+ self._forward_port(True, _policy, transaction, port, protocol, -+ toport, toaddr) -+ -+ self.__register_forward_port(_obj, forward_id, timeout, sender) -+ transaction.add_fail(self.__unregister_forward_port, _obj, forward_id) -+ -+ if use_transaction is None: -+ transaction.execute(True) -+ -+ return _policy -+ -+ def __register_forward_port(self, _obj, forward_id, timeout, sender): -+ _obj.settings["forward_ports"][forward_id] = \ -+ self.__gen_settings(timeout, sender) -+ -+ def remove_forward_port(self, policy, port, protocol, toport=None, -+ toaddr=None, use_transaction=None): -+ _policy = self._fw.check_policy(policy) -+ self._fw.check_panic() -+ _obj = self._policies[_policy] -+ -+ forward_id = self.__forward_port_id(port, protocol, toport, toaddr) -+ if forward_id not in _obj.settings["forward_ports"]: -+ _name = _obj.derived_from_zone if _obj.derived_from_zone else _policy -+ raise FirewallError(errors.NOT_ENABLED, -+ "'%s:%s:%s:%s' not in '%s'" % \ -+ (port, protocol, toport, toaddr, _name)) -+ -+ if use_transaction is None: -+ transaction = self.new_transaction() -+ else: -+ transaction = use_transaction -+ -+ if _obj.applied: -+ self._forward_port(False, _policy, transaction, port, protocol, -+ toport, toaddr) -+ -+ transaction.add_post(self.__unregister_forward_port, _obj, forward_id) -+ -+ if use_transaction is None: -+ transaction.execute(True) -+ -+ return _policy -+ -+ def __unregister_forward_port(self, _obj, forward_id): -+ if forward_id in _obj.settings["forward_ports"]: -+ del _obj.settings["forward_ports"][forward_id] -+ -+ def query_forward_port(self, policy, port, protocol, toport=None, -+ toaddr=None): -+ forward_id = self.__forward_port_id(port, protocol, toport, toaddr) -+ return forward_id in self.get_settings(policy)["forward_ports"] -+ -+ def list_forward_ports(self, policy): -+ return list(self.get_settings(policy)["forward_ports"].keys()) -+ -+ # ICMP BLOCK -+ -+ def check_icmp_block(self, icmp): -+ self._fw.check_icmptype(icmp) -+ -+ def __icmp_block_id(self, icmp): -+ self.check_icmp_block(icmp) -+ return icmp -+ -+ def add_icmp_block(self, policy, icmp, timeout=0, sender=None, -+ use_transaction=None): -+ _policy = self._fw.check_policy(policy) -+ self._fw.check_timeout(timeout) -+ self._fw.check_panic() -+ _obj = self._policies[_policy] -+ -+ icmp_id = self.__icmp_block_id(icmp) -+ if icmp_id in _obj.settings["icmp_blocks"]: -+ _name = _obj.derived_from_zone if _obj.derived_from_zone else _policy -+ raise FirewallError(errors.ALREADY_ENABLED, -+ "'%s' already in '%s'" % (icmp, _name)) -+ -+ if use_transaction is None: -+ transaction = self.new_transaction() -+ else: -+ transaction = use_transaction -+ -+ if _obj.applied: -+ self._icmp_block(True, _policy, icmp, transaction) -+ -+ self.__register_icmp_block(_obj, icmp_id, timeout, sender) -+ transaction.add_fail(self.__unregister_icmp_block, _obj, icmp_id) -+ -+ if use_transaction is None: -+ transaction.execute(True) -+ -+ return _policy -+ -+ def __register_icmp_block(self, _obj, icmp_id, timeout, sender): -+ _obj.settings["icmp_blocks"][icmp_id] = \ -+ self.__gen_settings(timeout, sender) -+ -+ def remove_icmp_block(self, policy, icmp, use_transaction=None): -+ _policy = self._fw.check_policy(policy) -+ self._fw.check_panic() -+ _obj = self._policies[_policy] -+ -+ icmp_id = self.__icmp_block_id(icmp) -+ if icmp_id not in _obj.settings["icmp_blocks"]: -+ _name = _obj.derived_from_zone if _obj.derived_from_zone else _policy -+ raise FirewallError(errors.NOT_ENABLED, -+ "'%s' not in '%s'" % (icmp, _name)) -+ -+ if use_transaction is None: -+ transaction = self.new_transaction() -+ else: -+ transaction = use_transaction -+ -+ if _obj.applied: -+ self._icmp_block(False, _policy, icmp, transaction) -+ -+ transaction.add_post(self.__unregister_icmp_block, _obj, icmp_id) -+ -+ if use_transaction is None: -+ transaction.execute(True) -+ -+ return _policy -+ -+ def __unregister_icmp_block(self, _obj, icmp_id): -+ if icmp_id in _obj.settings["icmp_blocks"]: -+ del _obj.settings["icmp_blocks"][icmp_id] -+ -+ def query_icmp_block(self, policy, icmp): -+ return self.__icmp_block_id(icmp) in self.get_settings(policy)["icmp_blocks"] -+ -+ def list_icmp_blocks(self, policy): -+ return self.get_settings(policy)["icmp_blocks"].keys() -+ -+ # ICMP BLOCK INVERSION -+ -+ def __icmp_block_inversion_id(self): -+ return True -+ -+ def add_icmp_block_inversion(self, policy, sender=None, -+ use_transaction=None): -+ _policy = self._fw.check_policy(policy) -+ self._fw.check_panic() -+ _obj = self._policies[_policy] -+ -+ icmp_block_inversion_id = self.__icmp_block_inversion_id() -+ if icmp_block_inversion_id in _obj.settings["icmp_block_inversion"]: -+ _name = _obj.derived_from_zone if _obj.derived_from_zone else _policy -+ raise FirewallError( -+ errors.ALREADY_ENABLED, -+ "icmp-block-inversion already enabled in '%s'" % _name) -+ -+ if use_transaction is None: -+ transaction = self.new_transaction() -+ else: -+ transaction = use_transaction -+ -+ if _obj.applied: -+ # undo icmp blocks -+ for args in self.get_settings(_policy)["icmp_blocks"]: -+ self._icmp_block(False, _policy, args, transaction) -+ -+ self._icmp_block_inversion(False, _policy, transaction) -+ -+ self.__register_icmp_block_inversion(_obj, icmp_block_inversion_id, -+ sender) -+ transaction.add_fail(self.__undo_icmp_block_inversion, _policy, _obj, -+ icmp_block_inversion_id) -+ -+ # redo icmp blocks -+ if _obj.applied: -+ for args in self.get_settings(_policy)["icmp_blocks"]: -+ self._icmp_block(True, _policy, args, transaction) -+ -+ self._icmp_block_inversion(True, _policy, transaction) -+ -+ if use_transaction is None: -+ transaction.execute(True) -+ -+ return _policy -+ -+ def __register_icmp_block_inversion(self, _obj, icmp_block_inversion_id, -+ sender): -+ _obj.settings["icmp_block_inversion"][icmp_block_inversion_id] = \ -+ self.__gen_settings(0, sender) -+ -+ def __undo_icmp_block_inversion(self, _policy, _obj, icmp_block_inversion_id): -+ transaction = self.new_transaction() -+ -+ # undo icmp blocks -+ if _obj.applied: -+ for args in self.get_settings(_policy)["icmp_blocks"]: -+ self._icmp_block(False, _policy, args, transaction) -+ -+ if icmp_block_inversion_id in _obj.settings["icmp_block_inversion"]: -+ del _obj.settings["icmp_block_inversion"][icmp_block_inversion_id] -+ -+ # redo icmp blocks -+ if _obj.applied: -+ for args in self.get_settings(_policy)["icmp_blocks"]: -+ self._icmp_block(True, _policy, args, transaction) -+ -+ transaction.execute(True) -+ -+ def remove_icmp_block_inversion(self, policy, use_transaction=None): -+ _policy = self._fw.check_policy(policy) -+ self._fw.check_panic() -+ _obj = self._policies[_policy] -+ -+ icmp_block_inversion_id = self.__icmp_block_inversion_id() -+ if icmp_block_inversion_id not in _obj.settings["icmp_block_inversion"]: -+ _name = _obj.derived_from_zone if _obj.derived_from_zone else _policy -+ raise FirewallError( -+ errors.NOT_ENABLED, -+ "icmp-block-inversion not enabled in '%s'" % _name) -+ -+ if use_transaction is None: -+ transaction = self.new_transaction() -+ else: -+ transaction = use_transaction -+ -+ if _obj.applied: -+ # undo icmp blocks -+ for args in self.get_settings(_policy)["icmp_blocks"]: -+ self._icmp_block(False, _policy, args, transaction) -+ -+ self._icmp_block_inversion(False, _policy, transaction) -+ -+ self.__unregister_icmp_block_inversion(_obj, -+ icmp_block_inversion_id) -+ transaction.add_fail(self.__register_icmp_block_inversion, _obj, -+ icmp_block_inversion_id, None) -+ -+ # redo icmp blocks -+ if _obj.applied: -+ for args in self.get_settings(_policy)["icmp_blocks"]: -+ self._icmp_block(True, _policy, args, transaction) -+ -+ self._icmp_block_inversion(True, _policy, transaction) -+ -+ if use_transaction is None: -+ transaction.execute(True) -+ -+ return _policy -+ -+ def __unregister_icmp_block_inversion(self, _obj, icmp_block_inversion_id): -+ if icmp_block_inversion_id in _obj.settings["icmp_block_inversion"]: -+ del _obj.settings["icmp_block_inversion"][icmp_block_inversion_id] -+ -+ def query_icmp_block_inversion(self, policy): -+ return self.__icmp_block_inversion_id() in \ -+ self.get_settings(policy)["icmp_block_inversion"] -+ -+ def gen_chain_rules(self, policy, create, table, chain, transaction): -+ # HACK: iptables backend has to support the (raw, PREROUTING) chain in -+ # multiple policies derived for zones - this is due to conntrack -+ # helpers. As such, track it in the policy matching zone --> HOST. -+ obj = self._fw.policy.get_policy(policy) -+ if obj.derived_from_zone and table == "raw" and chain == "PREROUTING": -+ for p in self._fw.zone._zone_policies[obj.derived_from_zone]: -+ p_obj = self._fw.policy.get_policy(p) -+ if p_obj.egress_zones[0] == "HOST": -+ tracking_policy = p -+ break -+ else: -+ tracking_policy = policy -+ -+ if create: -+ if tracking_policy in self._chains and \ -+ table in self._chains[tracking_policy]: -+ return -+ else: -+ if tracking_policy not in self._chains or \ -+ table not in self._chains[tracking_policy]: -+ return -+ -+ for backend in self._fw.enabled_backends(): -+ if backend.policies_supported and \ -+ table in backend.get_available_tables(): -+ rules = backend.build_policy_chain_rules(policy, table) -+ transaction.add_rules(backend, rules) -+ -+ self._register_chains(tracking_policy, create, [table]) -+ transaction.add_fail(self._register_chains, tracking_policy, not create, [table]) -+ -+ def _register_chains(self, policy, create, tables): -+ for table in tables: -+ if create: -+ self._chains.setdefault(policy, []).append(table) -+ else: -+ self._chains[policy].remove(table) -+ if len(self._chains[policy]) == 0: -+ del self._chains[policy] -+ -+ # IPSETS -+ -+ def _ipset_family(self, name): -+ if self._fw.ipset.get_type(name) == "hash:mac": -+ return None -+ return self._fw.ipset.get_family(name) -+ -+ def __ipset_type(self, name): -+ return self._fw.ipset.get_type(name) -+ -+ def _ipset_match_flags(self, name, flag): -+ return ",".join([flag] * self._fw.ipset.get_dimension(name)) -+ -+ def _check_ipset_applied(self, name): -+ return self._fw.ipset.check_applied(name) -+ -+ def _check_ipset_type_for_source(self, name): -+ _type = self.__ipset_type(name) -+ if _type not in SOURCE_IPSET_TYPES: -+ raise FirewallError( -+ errors.INVALID_IPSET, -+ "ipset '%s' with type '%s' not usable as source" % \ -+ (name, _type)) -+ -+ def _rule_prepare(self, enable, policy, rule, transaction): -+ if rule.family is not None: -+ ipvs = [ rule.family ] -+ else: -+ ipvs = [ipv for ipv in ["ipv4", "ipv6"] if self._fw.is_ipv_enabled(ipv)] -+ -+ source_ipv = self._rule_source_ipv(rule.source) -+ if source_ipv is not None and source_ipv != "": -+ if rule.family is not None: -+ # rule family is defined by user, no way to change it -+ if rule.family != source_ipv: -+ raise FirewallError(errors.INVALID_RULE, -+ "Source address family '%s' conflicts with rule family '%s'." % (source_ipv, rule.family)) -+ else: -+ # use the source family as rule family -+ ipvs = [ source_ipv ] -+ -+ # add an element to object to allow backends to know what ipvs this applies to -+ rule.ipvs = ipvs -+ -+ for backend in set([self._fw.get_backend_by_ipv(x) for x in ipvs]): -+ # SERVICE -+ if type(rule.element) == Rich_Service: -+ svc = self._fw.service.get_service(rule.element.name) -+ -+ destinations = [] -+ if len(svc.destination) > 0: -+ if rule.destination: -+ # we can not use two destinations at the same time -+ raise FirewallError(errors.INVALID_RULE, -+ "Destination conflict with service.") -+ for ipv in ipvs: -+ if ipv in svc.destination and backend.is_ipv_supported(ipv): -+ destinations.append(svc.destination[ipv]) -+ else: -+ # dummy for the following for loop -+ destinations.append(None) -+ -+ for destination in destinations: -+ if enable: -+ transaction.add_chain(policy, "filter", "INPUT") -+ transaction.add_chain(policy, "raw", "PREROUTING") -+ -+ if type(rule.action) == Rich_Accept: -+ # only load modules for accept action -+ helpers = self.get_helpers_for_service_modules(svc.modules, -+ enable) -+ helpers += self.get_helpers_for_service_helpers(svc.helpers) -+ helpers = sorted(set(helpers), key=lambda x: x.name) -+ -+ modules = [ ] -+ for helper in helpers: -+ module = helper.module -+ _module_short_name = get_nf_conntrack_short_name(module) -+ nat_module = module.replace("conntrack", "nat") -+ modules.append(nat_module) -+ if helper.family != "" and not backend.is_ipv_supported(helper.family): -+ # no support for family ipv, continue -+ continue -+ if len(helper.ports) < 1: -+ modules.append(module) -+ else: -+ for (port,proto) in helper.ports: -+ rules = backend.build_policy_helper_ports_rules( -+ enable, policy, proto, port, -+ destination, helper.name, _module_short_name) -+ transaction.add_rules(backend, rules) -+ transaction.add_modules(modules) -+ -+ # create rules -+ for (port,proto) in svc.ports: -+ if enable and type(rule.action) == Rich_Mark: -+ transaction.add_chain(policy, "mangle", "PREROUTING") -+ rules = backend.build_policy_ports_rules( -+ enable, policy, proto, port, destination, rule) -+ transaction.add_rules(backend, rules) -+ -+ for proto in svc.protocols: -+ if enable and type(rule.action) == Rich_Mark: -+ transaction.add_chain(policy, "mangle", "PREROUTING") -+ rules = backend.build_policy_protocol_rules( -+ enable, policy, proto, destination, rule) -+ transaction.add_rules(backend, rules) -+ -+ # create rules -+ for (port,proto) in svc.source_ports: -+ if enable and type(rule.action) == Rich_Mark: -+ transaction.add_chain(policy, "mangle", "PREROUTING") -+ rules = backend.build_policy_source_ports_rules( -+ enable, policy, proto, port, destination, rule) -+ transaction.add_rules(backend, rules) -+ -+ # PORT -+ elif type(rule.element) == Rich_Port: -+ port = rule.element.port -+ protocol = rule.element.protocol -+ self.check_port(port, protocol) -+ -+ if enable: -+ transaction.add_chain(policy, "filter", "INPUT") -+ if enable and type(rule.action) == Rich_Mark: -+ transaction.add_chain(policy, "mangle", "PREROUTING") -+ -+ rules = backend.build_policy_ports_rules( -+ enable, policy, protocol, port, None, rule) -+ transaction.add_rules(backend, rules) -+ -+ # PROTOCOL -+ elif type(rule.element) == Rich_Protocol: -+ protocol = rule.element.value -+ self.check_protocol(protocol) -+ -+ if enable: -+ transaction.add_chain(policy, "filter", "INPUT") -+ if enable and type(rule.action) == Rich_Mark: -+ transaction.add_chain(policy, "mangle", "PREROUTING") -+ -+ rules = backend.build_policy_protocol_rules( -+ enable, policy, protocol, None, rule) -+ transaction.add_rules(backend, rules) -+ -+ # MASQUERADE -+ elif type(rule.element) == Rich_Masquerade: -+ if enable: -+ transaction.add_chain(policy, "nat", "POSTROUTING") -+ transaction.add_chain(policy, "filter", "FORWARD_OUT") -+ for ipv in ipvs: -+ if backend.is_ipv_supported(ipv): -+ transaction.add_post(enable_ip_forwarding, ipv) -+ -+ rules = backend.build_policy_masquerade_rules(enable, policy, rule) -+ transaction.add_rules(backend, rules) -+ -+ # FORWARD PORT -+ elif type(rule.element) == Rich_ForwardPort: -+ port = rule.element.port -+ protocol = rule.element.protocol -+ toport = rule.element.to_port -+ toaddr = rule.element.to_address -+ for ipv in ipvs: -+ if backend.is_ipv_supported(ipv): -+ self.check_forward_port(ipv, port, protocol, toport, toaddr) -+ if toaddr and enable: -+ transaction.add_post(enable_ip_forwarding, ipv) -+ -+ if enable: -+ transaction.add_chain(policy, "nat", "PREROUTING") -+ -+ rules = backend.build_policy_forward_port_rules( -+ enable, policy, port, protocol, toport, -+ toaddr, rule) -+ transaction.add_rules(backend, rules) -+ -+ # SOURCE PORT -+ elif type(rule.element) == Rich_SourcePort: -+ port = rule.element.port -+ protocol = rule.element.protocol -+ self.check_port(port, protocol) -+ -+ if enable: -+ transaction.add_chain(policy, "filter", "INPUT") -+ if enable and type(rule.action) == Rich_Mark: -+ transaction.add_chain(policy, "mangle", "PREROUTING") -+ -+ rules = backend.build_policy_source_ports_rules( -+ enable, policy, protocol, port, None, rule) -+ transaction.add_rules(backend, rules) -+ -+ # ICMP BLOCK and ICMP TYPE -+ elif type(rule.element) == Rich_IcmpBlock or \ -+ type(rule.element) == Rich_IcmpType: -+ ict = self._fw.icmptype.get_icmptype(rule.element.name) -+ -+ if type(rule.element) == Rich_IcmpBlock and \ -+ rule.action and type(rule.action) == Rich_Accept: -+ # icmp block might have reject or drop action, but not accept -+ raise FirewallError(errors.INVALID_RULE, -+ "IcmpBlock not usable with accept action") -+ if ict.destination: -+ for ipv in ipvs: -+ if ipv in ict.destination \ -+ and not backend.is_ipv_supported(ipv): -+ raise FirewallError( -+ errors.INVALID_RULE, -+ "Icmp%s %s not usable with %s" % \ -+ ("Block" if type(rule.element) == \ -+ Rich_IcmpBlock else "Type", -+ rule.element.name, backend.name)) -+ -+ table = "filter" -+ if enable: -+ transaction.add_chain(policy, table, "INPUT") -+ transaction.add_chain(policy, table, "FORWARD_IN") -+ -+ rules = backend.build_policy_icmp_block_rules(enable, policy, ict, rule) -+ transaction.add_rules(backend, rules) -+ -+ elif rule.element is None: -+ if enable: -+ transaction.add_chain(policy, "filter", "INPUT") -+ if enable and type(rule.action) == Rich_Mark: -+ transaction.add_chain(policy, "mangle", "PREROUTING") -+ -+ rules = backend.build_policy_rich_source_destination_rules( -+ enable, policy, rule) -+ transaction.add_rules(backend, rules) -+ -+ # EVERYTHING ELSE -+ else: -+ raise FirewallError(errors.INVALID_RULE, "Unknown element %s" % -+ type(rule.element)) -+ -+ def _service(self, enable, policy, service, transaction, included_services=None): -+ svc = self._fw.service.get_service(service) -+ helpers = self.get_helpers_for_service_modules(svc.modules, enable) -+ helpers += self.get_helpers_for_service_helpers(svc.helpers) -+ helpers = sorted(set(helpers), key=lambda x: x.name) -+ -+ # First apply any services this service may include -+ if included_services is None: -+ included_services = [service] -+ for include in svc.includes: -+ if include in included_services: -+ continue -+ self.check_service(include) -+ included_services.append(include) -+ self._service(enable, policy, include, transaction, included_services=included_services) -+ -+ if enable: -+ transaction.add_chain(policy, "raw", "PREROUTING") -+ transaction.add_chain(policy, "filter", "INPUT") -+ -+ # build a list of (backend, destination). The destination may be ipv4, -+ # ipv6 or None -+ # -+ backends_ipv = [] -+ for ipv in ["ipv4", "ipv6"]: -+ if not self._fw.is_ipv_enabled(ipv): -+ continue -+ backend = self._fw.get_backend_by_ipv(ipv) -+ if len(svc.destination) > 0: -+ if ipv in svc.destination: -+ backends_ipv.append((backend, svc.destination[ipv])) -+ else: -+ if (backend, None) not in backends_ipv: -+ backends_ipv.append((backend, None)) -+ -+ for (backend,destination) in backends_ipv: -+ for helper in helpers: -+ module = helper.module -+ _module_short_name = get_nf_conntrack_short_name(module) -+ nat_module = helper.module.replace("conntrack", "nat") -+ transaction.add_module(nat_module) -+ if helper.family != "" and not backend.is_ipv_supported(helper.family): -+ # no support for family ipv, continue -+ continue -+ if len(helper.ports) < 1: -+ transaction.add_module(module) -+ else: -+ for (port,proto) in helper.ports: -+ rules = backend.build_policy_helper_ports_rules( -+ enable, policy, proto, port, -+ destination, helper.name, _module_short_name) -+ transaction.add_rules(backend, rules) -+ -+ for (port,proto) in svc.ports: -+ rules = backend.build_policy_ports_rules(enable, policy, proto, -+ port, destination) -+ transaction.add_rules(backend, rules) -+ -+ for protocol in svc.protocols: -+ rules = backend.build_policy_protocol_rules( -+ enable, policy, protocol, destination) -+ transaction.add_rules(backend, rules) -+ -+ for (port,proto) in svc.source_ports: -+ rules = backend.build_policy_source_ports_rules( -+ enable, policy, proto, port, destination) -+ transaction.add_rules(backend, rules) -+ -+ def _port(self, enable, policy, port, protocol, transaction): -+ if enable: -+ transaction.add_chain(policy, "filter", "INPUT") -+ -+ for backend in self._fw.enabled_backends(): -+ if not backend.policies_supported: -+ continue -+ -+ rules = backend.build_policy_ports_rules(enable, policy, protocol, -+ port) -+ transaction.add_rules(backend, rules) -+ -+ def _protocol(self, enable, policy, protocol, transaction): -+ if enable: -+ transaction.add_chain(policy, "filter", "INPUT") -+ -+ for backend in self._fw.enabled_backends(): -+ if not backend.policies_supported: -+ continue -+ -+ rules = backend.build_policy_protocol_rules(enable, policy, protocol) -+ transaction.add_rules(backend, rules) -+ -+ def _source_port(self, enable, policy, port, protocol, transaction): -+ if enable: -+ transaction.add_chain(policy, "filter", "INPUT") -+ -+ for backend in self._fw.enabled_backends(): -+ if not backend.policies_supported: -+ continue -+ -+ rules = backend.build_policy_source_ports_rules(enable, policy, protocol, port) -+ transaction.add_rules(backend, rules) -+ -+ def _masquerade(self, enable, policy, transaction): -+ if enable: -+ transaction.add_chain(policy, "nat", "POSTROUTING") -+ transaction.add_chain(policy, "filter", "FORWARD_OUT") -+ -+ ipv = "ipv4" -+ transaction.add_post(enable_ip_forwarding, ipv) -+ -+ backend = self._fw.get_backend_by_ipv(ipv) -+ rules = backend.build_policy_masquerade_rules(enable, policy) -+ transaction.add_rules(backend, rules) -+ -+ def _forward_port(self, enable, policy, transaction, port, protocol, -+ toport=None, toaddr=None): -+ if check_single_address("ipv6", toaddr): -+ ipv = "ipv6" -+ else: -+ ipv = "ipv4" -+ -+ if enable: -+ transaction.add_chain(policy, "nat", "PREROUTING") -+ -+ if toaddr and enable: -+ transaction.add_post(enable_ip_forwarding, ipv) -+ backend = self._fw.get_backend_by_ipv(ipv) -+ rules = backend.build_policy_forward_port_rules( -+ enable, policy, port, protocol, toport, -+ toaddr) -+ transaction.add_rules(backend, rules) -+ -+ def _icmp_block(self, enable, policy, icmp, transaction): -+ ict = self._fw.icmptype.get_icmptype(icmp) -+ -+ if enable: -+ transaction.add_chain(policy, "filter", "INPUT") -+ transaction.add_chain(policy, "filter", "FORWARD_IN") -+ -+ for backend in self._fw.enabled_backends(): -+ if not backend.policies_supported: -+ continue -+ skip_backend = False -+ -+ if ict.destination: -+ for ipv in ["ipv4", "ipv6"]: -+ if ipv in ict.destination: -+ if not backend.is_ipv_supported(ipv): -+ skip_backend = True -+ break -+ -+ if skip_backend: -+ continue -+ -+ rules = backend.build_policy_icmp_block_rules(enable, policy, ict) -+ transaction.add_rules(backend, rules) -+ -+ def _icmp_block_inversion(self, enable, policy, transaction): -+ target = self._policies[policy].target -+ -+ # Do not add general icmp accept rules into a trusted, block or drop -+ # policy. -+ if target in [ "DROP", "%%REJECT%%", "REJECT" ]: -+ return -+ if not self.query_icmp_block_inversion(policy) and target == "ACCEPT": -+ # ibi target and policy target are ACCEPT, no need to add an extra -+ # rule -+ return -+ -+ transaction.add_chain(policy, "filter", "INPUT") -+ transaction.add_chain(policy, "filter", "FORWARD_IN") -+ -+ for backend in self._fw.enabled_backends(): -+ if not backend.policies_supported: -+ continue -+ -+ rules = backend.build_policy_icmp_block_inversion_rules(enable, policy) -+ transaction.add_rules(backend, rules) -+ -+ def _get_table_chains_for_zone_dispatch(self, policy): -+ """Create a list of (table, chain) needed for zone dispatch""" -+ obj = self._policies[policy] -+ if obj.egress_zones[0] == "HOST": -+ # zone --> Host -+ return [("filter", "INPUT")] -+ elif obj.egress_zones[0] == "ANY": -+ # zone --> any -+ return [("filter", "FORWARD_IN"), ("nat", "PREROUTING"), -+ ("mangle", "PREROUTING"), ("raw", "PREROUTING")] -+ elif obj.ingress_zones[0] == "ANY": -+ # any --> zone -+ return [("filter", "FORWARD_OUT"), ("nat", "POSTROUTING")] -+ else: -+ return FirewallError("Invalid policy: %s" % (policy)) -+ -+ def policy_base_chain_name(self, policy, table, policy_prefix): -+ obj = self._fw.policy.get_policy(policy) -+ if obj.derived_from_zone: -+ if obj.egress_zones[0] == "HOST": -+ # zone --> Host -+ if table == "filter": -+ return "IN_" + obj.derived_from_zone -+ if table == "raw": -+ # FIXME: nftables doesn't actually use this. Only iptables -+ return "PRE_" + obj.derived_from_zone -+ elif obj.egress_zones[0] == "ANY": -+ # zone --> any -+ if table == "filter": -+ return "FWDI_" + obj.derived_from_zone -+ elif table in ["nat", "mangle", "raw"]: -+ return "PRE_" + obj.derived_from_zone -+ elif obj.ingress_zones[0] == "ANY": -+ # any --> zone -+ if table == "filter": -+ return "FWDO_" + obj.derived_from_zone -+ elif table == "nat": -+ return "POST_" + obj.derived_from_zone -+ return FirewallError("Can't convert policy to chain name: %s" % (policy)) -+ else: -+ return policy_prefix + policy -diff --git a/src/firewall/core/fw_transaction.py b/src/firewall/core/fw_transaction.py -index cc8b1e1..7639cdb 100644 ---- a/src/firewall/core/fw_transaction.py -+++ b/src/firewall/core/fw_transaction.py -@@ -36,7 +36,7 @@ class FirewallTransaction(object): - self.pre_funcs = [ ] # [ (func, args),.. ] - self.post_funcs = [ ] # [ (func, args),.. ] - self.fail_funcs = [ ] # [ (func, args),.. ] -- self.chains = [ ] # [ (zone, table, chain),.. ] -+ self.chains = [ ] # [ (table, policy),.. ] - self.modules = [ ] # [ module,.. ] - - def clear(self): -@@ -68,16 +68,16 @@ class FirewallTransaction(object): - def add_fail(self, func, *args): - self.fail_funcs.append((func, args)) - -- def add_chain(self, zone, table, chain): -- ztc = (zone, table, chain) -- if ztc not in self.chains: -- self.fw.zone.gen_chain_rules(zone, True, table, chain, self) -- self.chains.append(ztc) -+ def add_chain(self, policy, table, chain): -+ tp = (table, policy) -+ if tp not in self.chains: -+ self.fw.policy.gen_chain_rules(policy, True, table, chain, self) -+ self.chains.append(tp) - -- def remove_chain(self, zone, table, chain): -- ztc = (zone, table, chain) -- if ztc in self.chains: -- self.chains.remove(ztc) -+ def remove_chain(self, policy, table, chain): -+ tp = (table, policy) -+ if tp in self.chains: -+ self.chains.remove(tp) - - def add_module(self, module): - if module not in self.modules: -diff --git a/src/firewall/core/fw_zone.py b/src/firewall/core/fw_zone.py -index d32d7a8..2f47e33 100644 ---- a/src/firewall/core/fw_zone.py -+++ b/src/firewall/core/fw_zone.py -@@ -20,39 +20,39 @@ - # - - import time --from firewall.core.base import SHORTCUTS, DEFAULT_ZONE_TARGET, \ -- ZONE_SOURCE_IPSET_TYPES --from firewall.core.logger import log --from firewall.functions import portStr, checkIPnMask, checkIP6nMask, \ -- checkProtocol, enable_ip_forwarding, check_single_address, check_mac, \ -- portInPortRange, get_nf_conntrack_short_name, coalescePortRange, breakPortRange --from firewall.core.rich import Rich_Rule, Rich_Accept, \ -- Rich_Mark, Rich_Service, Rich_Port, Rich_Protocol, \ -- Rich_Masquerade, Rich_ForwardPort, Rich_SourcePort, Rich_IcmpBlock, \ -- Rich_IcmpType -+import copy -+from firewall.core.base import SHORTCUTS, DEFAULT_ZONE_TARGET, SOURCE_IPSET_TYPES - from firewall.core.fw_transaction import FirewallTransaction -+from firewall.core.io.policy import Policy -+from firewall.core.logger import log -+from firewall.core.rich import Rich_Service, Rich_Port, Rich_Protocol, Rich_SourcePort, Rich_ForwardPort, \ -+ Rich_IcmpBlock, Rich_IcmpType, Rich_Masquerade, Rich_Mark -+from firewall.functions import checkIPnMask, checkIP6nMask, check_mac - from firewall import errors - from firewall.errors import FirewallError - from firewall.fw_types import LastUpdatedOrderedDict - - class FirewallZone(object): -+ ZONE_POLICY_PRIORITY = 0 -+ - def __init__(self, fw): - self._fw = fw -- self._chains = { } - self._zones = { } -+ self._zone_policies = { } - - def __repr__(self): -- return '%s(%r, %r)' % (self.__class__, self._chains, self._zones) -+ return '%s(%r)' % (self.__class__, self._zones) - - def cleanup(self): -- self._chains.clear() - self._zones.clear() -- -- # transaction -+ self._zone_policies.clear() - - def new_transaction(self): - return FirewallTransaction(self._fw) - -+ def policy_name_from_zones(self, fromZone, toZone): -+ return "zone_{fromZone}_{toZone}".format(fromZone=fromZone, toZone=toZone) -+ - # zones - - def get_zones(self): -@@ -78,24 +78,77 @@ class FirewallZone(object): - z = self._fw.check_zone(zone) - return self._zones[z] - -- def _first_except(self, e, f, name, *args, **kwargs): -- try: -- f(name, *args, **kwargs) -- except FirewallError as error: -- if not e: -- return error -- return e -+ def policy_obj_from_zone_obj(self, z_obj, fromZone, toZone): -+ p_obj = Policy() -+ p_obj.derived_from_zone = z_obj.name -+ p_obj.name = self.policy_name_from_zones(fromZone, toZone) -+ p_obj.priority = self.ZONE_POLICY_PRIORITY -+ p_obj.target = z_obj.target -+ p_obj.ingress_zones = [fromZone] -+ p_obj.egress_zones = [toZone] -+ -+ # copy zone permanent config to policy permanent config -+ # WARN: This assumes the same attribute names. -+ # -+ for setting in ["services", "ports", -+ "masquerade", "forward_ports", -+ "source_ports", -+ "icmp_blocks", "rules", -+ "protocols"]: -+ if fromZone == z_obj.name and toZone == "HOST" and \ -+ setting in ["services", "ports", "source_ports", "icmp_blocks", "protocols"]: -+ # zone --> HOST -+ setattr(p_obj, setting, copy.deepcopy(getattr(z_obj, setting))) -+ elif fromZone == "ANY" and toZone == z_obj.name and setting in ["masquerade"]: -+ # any zone --> zone -+ setattr(p_obj, setting, copy.deepcopy(getattr(z_obj, setting))) -+ elif fromZone == z_obj.name and toZone == "ANY" and \ -+ setting in ["icmp_blocks", "forward_ports"]: -+ # zone --> any zone -+ setattr(p_obj, setting, copy.deepcopy(getattr(z_obj, setting))) -+ elif setting in ["rules"]: -+ p_obj.rules = [] -+ for rule in z_obj.rules: -+ current_policy = self.policy_name_from_zones(fromZone, toZone) -+ -+ if current_policy in self._rich_rule_to_policies(z_obj.name, rule): -+ p_obj.rules.append(copy.deepcopy(rule)) -+ -+ return p_obj - - def add_zone(self, obj): - obj.settings = { x : LastUpdatedOrderedDict() -- for x in [ "interfaces", "sources", -- "services", "ports", -- "masquerade", "forward_ports", -- "source_ports", -- "icmp_blocks", "rules", -- "protocols", "icmp_block_inversion" ] } -- -+ for x in ["interfaces", "sources", -+ "icmp_block_inversion"] } - self._zones[obj.name] = obj -+ self._zone_policies[obj.name] = [] -+ -+ # Create policy objects, will need many: -+ # - (zone --> HOST) - ports, service, etc -+ # - (any zone --> zone) - masquerade -+ # - (zone --> any zone) - ICMP block, icmp block inversion -+ # - also includes forward-ports because it works on (nat, -+ # PREROUTING) and therefore applies to redirects to the local -+ # host or dnat to a different host. -+ # - also includes rich rule "mark" action for the same reason -+ # -+ for fromZone,toZone in [(obj.name, "HOST"), -+ ("ANY", obj.name), (obj.name, "ANY")]: -+ p_obj = self.policy_obj_from_zone_obj(obj, fromZone, toZone) -+ self._fw.policy.add_policy(p_obj) -+ self._zone_policies[obj.name].append(p_obj.name) -+ -+ self.copy_permanent_to_runtime(obj.name) -+ -+ def copy_permanent_to_runtime(self, zone): -+ obj = self._zones[zone] -+ -+ for arg in obj.interfaces: -+ self.add_interface(zone, arg, allow_apply=False) -+ for arg in obj.sources: -+ self.add_source(zone, arg, allow_apply=False) -+ if obj.icmp_block_inversion: -+ self.add_icmp_block_inversion(zone) - - def remove_zone(self, zone): - obj = self._zones[zone] -@@ -103,68 +156,14 @@ class FirewallZone(object): - self.unapply_zone_settings(zone) - obj.settings.clear() - del self._zones[zone] -+ del self._zone_policies[zone] - - def apply_zones(self, use_transaction=None): -- if use_transaction is None: -- transaction = self.new_transaction() -- else: -- transaction = use_transaction -- -- error = None - for zone in self.get_zones(): -- obj = self._zones[zone] -- -- # register icmp block inversion setting but don't apply -- if obj.icmp_block_inversion: -- error = self._first_except(error, self.add_icmp_block_inversion, obj.name, -- use_transaction=transaction) -- -- if len(obj.interfaces) > 0 or len(obj.sources) > 0: -- obj.applied = True -- -- log.debug1("Applying zone '%s'", obj.name) -- -- # load zone in case of missing services, icmptypes etc. -- for args in obj.icmp_blocks: -- error = self._first_except(error, self.add_icmp_block, obj.name, args, -- use_transaction=transaction) -- for args in obj.forward_ports: -- error = self._first_except(error, self.add_forward_port, obj.name, *args, -- use_transaction=transaction) -- for args in obj.services: -- error = self._first_except(error, self.add_service, obj.name, args, -- use_transaction=transaction) -- for args in obj.ports: -- error = self._first_except(error, self.add_port, obj.name, *args, -- use_transaction=transaction) -- for args in obj.protocols: -- error = self._first_except(error, self.add_protocol, obj.name, args, -- use_transaction=transaction) -- for args in obj.source_ports: -- error = self._first_except(error, self.add_source_port, obj.name, *args, -- use_transaction=transaction) -- if obj.masquerade: -- error = self._first_except(error, self.add_masquerade, obj.name, -- use_transaction=transaction) -- for args in obj.rules: -- error = self._first_except(error, self.add_rule, obj.name, args, -- use_transaction=transaction) -- for args in obj.interfaces: -- error = self._first_except(error, self.add_interface, obj.name, args, -- use_transaction=transaction) -- for args in obj.sources: -- error = self._first_except(error, self.add_source, obj.name, args, -- use_transaction=transaction) -- # apply icmp accept/reject rule always -- if obj.applied: -- error = self._first_except(error, self._icmp_block_inversion, True, -- obj.name, transaction) -- -- if use_transaction is None: -- transaction.execute(True) -- -- if error: -- raise error -+ z_obj = self._zones[zone] -+ if len(z_obj.interfaces) > 0 or len(z_obj.sources) > 0: -+ log.debug1("Applying zone '%s'", zone) -+ self.apply_zone_settings(zone, use_transaction=use_transaction) - - def set_zone_applied(self, zone, applied): - obj = self._zones[zone] -@@ -212,19 +211,6 @@ class FirewallZone(object): - if use_transaction is None: - transaction.execute(True) - -- # dynamic chain handling -- -- def _register_chains(self, zone, create, chains): -- for (table, chain) in chains: -- if create: -- self._chains.setdefault(zone, { }).setdefault(table, [ ]).append(chain) -- else: -- self._chains[zone][table].remove(chain) -- if len(self._chains[zone][table]) == 0: -- del self._chains[zone][table] -- if len(self._chains[zone]) == 0: -- del self._chains[zone] -- - # settings - - # generate settings record with sender, timeout -@@ -239,114 +225,62 @@ class FirewallZone(object): - def get_settings(self, zone): - return self.get_zone(zone).settings - -- def set_settings(self, zone, settings): -- _obj = self.get_zone(zone) -- -- try: -- for key in settings: -- for args in settings[key]: -- if args in _obj.settings[key]: -- # do not add things, that are already active in the -- # zone configuration, also do not restore date, -- # sender and timeout -- continue -- if key == "icmp_blocks": -- self.add_icmp_block(zone, args) -- elif key == "forward_ports": -- self.add_forward_port(zone, *args) -- elif key == "services": -- self.add_service(zone, args) -- elif key == "ports": -- self.add_port(zone, *args) -- elif key == "protocols": -- self.add_protocol(zone, *args) -- elif key == "source_ports": -- self.add_source_port(zone, *args) -- elif key == "masquerade": -- self.add_masquerade(zone) -- elif key == "rules": -- self.add_rule(zone, Rich_Rule(rule_str=args)) -- elif key == "interfaces": -- self.change_zone_of_interface(zone, args) -- elif key == "sources": -- self.change_zone_of_source(zone, args) -- else: -- log.warning("Zone '%s': Unknown setting '%s:%s', " -- "unable to restore.", zone, key, args) -- # restore old date, sender and timeout -- if args in _obj.settings[key]: -- _obj.settings[key][args] = settings[key][args] -- -- except FirewallError as msg: -- log.warning(str(msg)) -- -- def __zone_settings(self, enable, zone, use_transaction=None): -+ def _zone_settings(self, enable, zone, transaction): -+ settings = self.get_settings(zone) -+ for key in settings: -+ for args in settings[key]: -+ if key == "interfaces": -+ self._interface(enable, zone, args, transaction) -+ elif key == "sources": -+ self._source(enable, zone, args[0], args[1], transaction) -+ elif key == "icmp_block_inversion": -+ continue -+ else: -+ log.warning("Zone '%s': Unknown setting '%s:%s', " -+ "unable to apply", zone, key, args) -+ # ICMP-block-inversion is always applied -+ if enable: -+ self._icmp_block_inversion(enable, zone, transaction) -+ -+ def apply_zone_settings(self, zone, use_transaction=None): - _zone = self._fw.check_zone(zone) - obj = self._zones[_zone] -- if (enable and obj.applied) or (not enable and not obj.applied): -+ if obj.applied: - return -- if enable: -- obj.applied = True -+ obj.applied = True - - if use_transaction is None: - transaction = self.new_transaction() - else: - transaction = use_transaction - -- settings = self.get_settings(zone) -- for key in settings: -- for args in settings[key]: -- try: -- if key == "icmp_blocks": -- self._icmp_block(enable, _zone, args, transaction) -- elif key == "icmp_block_inversion": -- continue -- elif key == "forward_ports": -- self._forward_port(enable, _zone, transaction, -- *args) -- elif key == "services": -- self._service(enable, _zone, args, transaction) -- elif key == "ports": -- self._port(enable, _zone, args[0], args[1], -- transaction) -- elif key == "protocols": -- self._protocol(enable, _zone, args, transaction) -- elif key == "source_ports": -- self._source_port(enable, _zone, args[0], args[1], -- transaction) -- elif key == "masquerade": -- self._masquerade(enable, _zone, transaction) -- elif key == "rules": -- self.__rule(enable, _zone, Rich_Rule(rule_str=args), -- transaction) -- elif key == "interfaces": -- self._interface(enable, _zone, args, transaction) -- elif key == "sources": -- self._source(enable, _zone, args[0], args[1], -- transaction) -- else: -- log.warning("Zone '%s': Unknown setting '%s:%s', " -- "unable to apply", zone, key, args) -- except FirewallError as msg: -- log.warning(str(msg)) -+ for policy in self._zone_policies[_zone]: -+ log.debug1("Applying policy (%s) derived from zone '%s'", policy, zone) -+ self._fw.policy.apply_policy_settings(policy, use_transaction=transaction) - -- if enable: -- # add icmp rule(s) always -- self._icmp_block_inversion(True, obj.name, transaction) -+ self._zone_settings(True, _zone, transaction) - - if use_transaction is None: -- transaction.execute(enable) -- -- def apply_zone_settings(self, zone, use_transaction=None): -- self.__zone_settings(True, zone, use_transaction) -+ transaction.execute(True) - - def unapply_zone_settings(self, zone, use_transaction=None): -- self.__zone_settings(False, zone, use_transaction) -+ _zone = self._fw.check_zone(zone) -+ obj = self._zones[_zone] -+ if not obj.applied: -+ return - -- def unapply_zone_settings_if_unused(self, zone): -- obj = self._zones[zone] -- if len(obj.interfaces) == 0 and len(obj.sources) == 0: -- self.unapply_zone_settings(zone) -+ if use_transaction is None: -+ transaction = self.new_transaction() -+ else: -+ transaction = use_transaction -+ -+ for policy in self._zone_policies[_zone]: -+ self._fw.policy.unapply_policy_settings(policy, use_transaction=transaction) -+ -+ self._zone_settings(False, _zone, transaction) -+ -+ if use_transaction is None: -+ transaction.execute(True) - - def get_config_with_settings(self, zone): - """ -@@ -390,7 +324,7 @@ class FirewallZone(object): - return interface - - def add_interface(self, zone, interface, sender=None, -- use_transaction=None): -+ use_transaction=None, allow_apply=True): - self._fw.check_panic() - _zone = self._fw.check_zone(zone) - _obj = self._zones[_zone] -@@ -413,12 +347,13 @@ class FirewallZone(object): - else: - transaction = use_transaction - -- if not _obj.applied: -+ if not _obj.applied and allow_apply: - self.apply_zone_settings(zone, - use_transaction=transaction) - transaction.add_fail(self.set_zone_applied, _zone, False) - -- self._interface(True, _zone, interface, transaction) -+ if allow_apply: -+ self._interface(True, _zone, interface, transaction) - - self.__register_interface(_obj, interface_id, zone, sender) - transaction.add_fail(self.__unregister_interface, _obj, -@@ -494,7 +429,6 @@ class FirewallZone(object): - if use_transaction is None: - transaction.execute(True) - --# self.unapply_zone_settings_if_unused(_zone) - return _zone - - def __unregister_interface(self, _obj, interface_id): -@@ -509,7 +443,7 @@ class FirewallZone(object): - - # SOURCES - -- def check_source(self, source): -+ def check_source(self, source, applied=False): - if checkIPnMask(source): - return "ipv4" - elif checkIP6nMask(source): -@@ -518,16 +452,18 @@ class FirewallZone(object): - return "" - elif source.startswith("ipset:"): - self._check_ipset_type_for_source(source[6:]) -- self._check_ipset_applied(source[6:]) -+ if applied: -+ self._check_ipset_applied(source[6:]) - return self._ipset_family(source[6:]) - else: - raise FirewallError(errors.INVALID_ADDR, source) - -- def __source_id(self, source): -- ipv = self.check_source(source) -+ def __source_id(self, source, applied=False): -+ ipv = self.check_source(source, applied=applied) - return (ipv, source) - -- def add_source(self, zone, source, sender=None, use_transaction=None): -+ def add_source(self, zone, source, sender=None, use_transaction=None, -+ allow_apply=True): - self._fw.check_panic() - _zone = self._fw.check_zone(zone) - _obj = self._zones[_zone] -@@ -535,7 +471,7 @@ class FirewallZone(object): - if check_mac(source): - source = source.upper() - -- source_id = self.__source_id(source) -+ source_id = self.__source_id(source, applied=allow_apply) - - if source_id in _obj.settings["sources"]: - raise FirewallError(errors.ZONE_ALREADY_SET, -@@ -549,12 +485,13 @@ class FirewallZone(object): - else: - transaction = use_transaction - -- if not _obj.applied: -+ if not _obj.applied and allow_apply: - self.apply_zone_settings(zone, - use_transaction=transaction) - transaction.add_fail(self.set_zone_applied, _zone, False) - -- self._source(True, _zone, source_id[0], source_id[1], transaction) -+ if allow_apply: -+ self._source(True, _zone, source_id[0], source_id[1], transaction) - - self.__register_source(_obj, source_id, zone, sender) - transaction.add_fail(self.__unregister_source, _obj, source_id) -@@ -617,7 +554,6 @@ class FirewallZone(object): - if use_transaction is None: - transaction.execute(True) - --# self.unapply_zone_settings_if_unused(_zone) - return _zone - - def __unregister_source(self, _obj, source_id): -@@ -632,1328 +568,296 @@ class FirewallZone(object): - def list_sources(self, zone): - return [ k[1] for k in self.get_settings(zone)["sources"].keys() ] - -- # RICH LANGUAGE -+ def _interface(self, enable, zone, interface, transaction, append=False): -+ for backend in self._fw.enabled_backends(): -+ if not backend.policies_supported: -+ continue -+ for policy in self._zone_policies[zone]: -+ for (table, chain) in self._fw.policy._get_table_chains_for_zone_dispatch(policy): -+ # create needed chains if not done already -+ if enable: -+ transaction.add_chain(policy, table, chain) - -- def check_rule(self, rule): -- rule.check() -+ rules = backend.build_zone_source_interface_rules(enable, -+ zone, policy, interface, table, chain, append) -+ transaction.add_rules(backend, rules) - -- def __rule_id(self, rule): -- self.check_rule(rule) -- return str(rule) -+ # IPSETS - -- def _rule_source_ipv(self, source): -- if not source: -+ def _ipset_family(self, name): -+ if self._ipset_type(name) == "hash:mac": - return None -+ return self._fw.ipset.get_family(name, applied=False) - -- if source.addr: -- if checkIPnMask(source.addr): -- return "ipv4" -- elif checkIP6nMask(source.addr): -- return "ipv6" -- elif hasattr(source, "mac") and source.mac: -- return "" -- elif hasattr(source, "ipset") and source.ipset: -- self._check_ipset_type_for_source(source.ipset) -- self._check_ipset_applied(source.ipset) -- return self._ipset_family(source.ipset) -- -- return None -- -- def __rule(self, enable, zone, rule, transaction): -- self._rule_prepare(enable, zone, rule, transaction) -- -- def add_rule(self, zone, rule, timeout=0, sender=None, -- use_transaction=None): -- _zone = self._fw.check_zone(zone) -- self._fw.check_timeout(timeout) -- self._fw.check_panic() -- _obj = self._zones[_zone] -- -- rule_id = self.__rule_id(rule) -- if rule_id in _obj.settings["rules"]: -- raise FirewallError(errors.ALREADY_ENABLED, -- "'%s' already in '%s'" % (rule, _zone)) -- -- if use_transaction is None: -- transaction = self.new_transaction() -- else: -- transaction = use_transaction -- -- if _obj.applied: -- self.__rule(True, _zone, rule, transaction) -- -- self.__register_rule(_obj, rule_id, timeout, sender) -- transaction.add_fail(self.__unregister_rule, _obj, rule_id) -- -- if use_transaction is None: -- transaction.execute(True) -- -- return _zone -- -- def __register_rule(self, _obj, rule_id, timeout, sender): -- _obj.settings["rules"][rule_id] = self.__gen_settings( -- timeout, sender) -- -- def remove_rule(self, zone, rule, -- use_transaction=None): -- _zone = self._fw.check_zone(zone) -- self._fw.check_panic() -- _obj = self._zones[_zone] -- -- rule_id = self.__rule_id(rule) -- if rule_id not in _obj.settings["rules"]: -- raise FirewallError(errors.NOT_ENABLED, -- "'%s' not in '%s'" % (rule, _zone)) -- -- if use_transaction is None: -- transaction = self.new_transaction() -- else: -- transaction = use_transaction -- -- if _obj.applied: -- self.__rule(False, _zone, rule, transaction) -- -- transaction.add_post(self.__unregister_rule, _obj, rule_id) -- -- if use_transaction is None: -- transaction.execute(True) -- -- return _zone -- -- def __unregister_rule(self, _obj, rule_id): -- if rule_id in _obj.settings["rules"]: -- del _obj.settings["rules"][rule_id] -- -- def query_rule(self, zone, rule): -- return self.__rule_id(rule) in self.get_settings(zone)["rules"] -- -- def list_rules(self, zone): -- return list(self.get_settings(zone)["rules"].keys()) -- -- # SERVICES -- -- def check_service(self, service): -- self._fw.check_service(service) -- -- def __service_id(self, service): -- self.check_service(service) -- return service -- -- def add_service(self, zone, service, timeout=0, sender=None, -- use_transaction=None): -- _zone = self._fw.check_zone(zone) -- self._fw.check_timeout(timeout) -- self._fw.check_panic() -- _obj = self._zones[_zone] -- -- service_id = self.__service_id(service) -- if service_id in _obj.settings["services"]: -- raise FirewallError(errors.ALREADY_ENABLED, -- "'%s' already in '%s'" % (service, _zone)) -- -- if use_transaction is None: -- transaction = self.new_transaction() -- else: -- transaction = use_transaction -- -- if _obj.applied: -- self._service(True, _zone, service, transaction) -- -- self.__register_service(_obj, service_id, timeout, sender) -- transaction.add_fail(self.__unregister_service, _obj, service_id) -- -- if use_transaction is None: -- transaction.execute(True) -- -- return _zone -- -- def __register_service(self, _obj, service_id, timeout, sender): -- _obj.settings["services"][service_id] = \ -- self.__gen_settings(timeout, sender) -- -- def remove_service(self, zone, service, -- use_transaction=None): -- _zone = self._fw.check_zone(zone) -- self._fw.check_panic() -- _obj = self._zones[_zone] -+ def _ipset_type(self, name): -+ return self._fw.ipset.get_type(name, applied=False) - -- service_id = self.__service_id(service) -- if service_id not in _obj.settings["services"]: -- raise FirewallError(errors.NOT_ENABLED, -- "'%s' not in '%s'" % (service, _zone)) -+ def _ipset_match_flags(self, name, flag): -+ return ",".join([flag] * self._fw.ipset.get_dimension(name)) - -- if use_transaction is None: -- transaction = self.new_transaction() -- else: -- transaction = use_transaction -+ def _check_ipset_applied(self, name): -+ return self._fw.ipset.check_applied(name) - -- if _obj.applied: -- self._service(False, _zone, service, transaction) -+ def _check_ipset_type_for_source(self, name): -+ _type = self._ipset_type(name) -+ if _type not in SOURCE_IPSET_TYPES: -+ raise FirewallError( -+ errors.INVALID_IPSET, -+ "ipset '%s' with type '%s' not usable as source" % \ -+ (name, _type)) - -- transaction.add_post(self.__unregister_service, _obj, service_id) -+ def _source(self, enable, zone, ipv, source, transaction): -+ # For mac source bindings ipv is an empty string, the mac source will -+ # be added for ipv4 and ipv6 -+ for backend in [self._fw.get_backend_by_ipv(ipv)] if ipv else self._fw.enabled_backends(): -+ if not backend.policies_supported: -+ continue -+ for policy in self._zone_policies[zone]: -+ for (table, chain) in self._fw.policy._get_table_chains_for_zone_dispatch(policy): -+ # create needed chains if not done already -+ if enable: -+ transaction.add_chain(policy, table, chain) - -- if use_transaction is None: -- transaction.execute(True) -+ rules = backend.build_zone_source_address_rules(enable, zone, -+ policy, source, table, chain) -+ transaction.add_rules(backend, rules) - -- return _zone -+ def add_service(self, zone, service, timeout=0, sender=None): -+ zone = self._fw.check_zone(zone) -+ p_name = self.policy_name_from_zones(zone, "HOST") -+ self._fw.policy.add_service(p_name, service, timeout, sender) -+ return zone - -- def __unregister_service(self, _obj, service_id): -- if service_id in _obj.settings["services"]: -- del _obj.settings["services"][service_id] -+ def remove_service(self, zone, service): -+ zone = self._fw.check_zone(zone) -+ p_name = self.policy_name_from_zones(zone, "HOST") -+ self._fw.policy.remove_service(p_name, service) -+ return zone - - def query_service(self, zone, service): -- return self.__service_id(service) in self.get_settings(zone)["services"] -+ zone = self._fw.check_zone(zone) -+ p_name = self.policy_name_from_zones(zone, "HOST") -+ return self._fw.policy.query_service(p_name, service) - - def list_services(self, zone): -- return self.get_settings(zone)["services"].keys() -- -- def get_helpers_for_service_helpers(self, helpers): -- _helpers = [ ] -- for helper in helpers: -- try: -- _helper = self._fw.helper.get_helper(helper) -- except FirewallError: -- raise FirewallError(errors.INVALID_HELPER, helper) -- _helpers.append(_helper) -- return _helpers -- -- def get_helpers_for_service_modules(self, modules, enable): -- # If automatic helper assignment is turned off, helpers that -- # do not have ports defined will be replaced by the helpers -- # that the helper.module defines. -- _helpers = [ ] -- for module in modules: -- try: -- helper = self._fw.helper.get_helper(module) -- except FirewallError: -- raise FirewallError(errors.INVALID_HELPER, module) -- if len(helper.ports) < 1: -- _module_short_name = get_nf_conntrack_short_name(helper.module) -- try: -- _helper = self._fw.helper.get_helper(_module_short_name) -- _helpers.append(_helper) -- except FirewallError: -- if enable: -- log.warning("Helper '%s' is not available" % _module_short_name) -- continue -- else: -- _helpers.append(helper) -- return _helpers -- -- # PORTS -- -- def check_port(self, port, protocol): -- self._fw.check_port(port) -- self._fw.check_tcpudp(protocol) -- -- def __port_id(self, port, protocol): -- self.check_port(port, protocol) -- return (portStr(port, "-"), protocol) -- -- def add_port(self, zone, port, protocol, timeout=0, sender=None, -- use_transaction=None): -- _zone = self._fw.check_zone(zone) -- self._fw.check_timeout(timeout) -- self._fw.check_panic() -- _obj = self._zones[_zone] -- -- existing_port_ids = list(filter(lambda x: x[1] == protocol, _obj.settings["ports"])) -- for port_id in existing_port_ids: -- if portInPortRange(port, port_id[0]): -- raise FirewallError(errors.ALREADY_ENABLED, -- "'%s:%s' already in '%s'" % (port, protocol, _zone)) -- -- added_ranges, removed_ranges = coalescePortRange(port, [_port for (_port, _protocol) in existing_port_ids]) -- -- if use_transaction is None: -- transaction = self.new_transaction() -- else: -- transaction = use_transaction -- -- if _obj.applied: -- for range in added_ranges: -- self._port(True, _zone, portStr(range, "-"), protocol, transaction) -- for range in removed_ranges: -- self._port(False, _zone, portStr(range, "-"), protocol, transaction) -- -- for range in added_ranges: -- port_id = self.__port_id(range, protocol) -- self.__register_port(_obj, port_id, timeout, sender) -- transaction.add_fail(self.__unregister_port, _obj, port_id) -- for range in removed_ranges: -- port_id = self.__port_id(range, protocol) -- transaction.add_post(self.__unregister_port, _obj, port_id) -- -- if use_transaction is None: -- transaction.execute(True) -- -- return _zone -- -- def __register_port(self, _obj, port_id, timeout, sender): -- _obj.settings["ports"][port_id] = \ -- self.__gen_settings(timeout, sender) -- -- def remove_port(self, zone, port, protocol, -- use_transaction=None): -- _zone = self._fw.check_zone(zone) -- self._fw.check_panic() -- _obj = self._zones[_zone] -- -- existing_port_ids = list(filter(lambda x: x[1] == protocol, _obj.settings["ports"])) -- for port_id in existing_port_ids: -- if portInPortRange(port, port_id[0]): -- break -- else: -- raise FirewallError(errors.NOT_ENABLED, -- "'%s:%s' not in '%s'" % (port, protocol, _zone)) -- -- added_ranges, removed_ranges = breakPortRange(port, [_port for (_port, _protocol) in existing_port_ids]) -- -- if use_transaction is None: -- transaction = self.new_transaction() -- else: -- transaction = use_transaction -- -- if _obj.applied: -- for range in added_ranges: -- self._port(True, _zone, portStr(range, "-"), protocol, transaction) -- for range in removed_ranges: -- self._port(False, _zone, portStr(range, "-"), protocol, transaction) -- -- for range in added_ranges: -- port_id = self.__port_id(range, protocol) -- self.__register_port(_obj, port_id, 0, None) -- transaction.add_fail(self.__unregister_port, _obj, port_id) -- for range in removed_ranges: -- port_id = self.__port_id(range, protocol) -- transaction.add_post(self.__unregister_port, _obj, port_id) -- -- if use_transaction is None: -- transaction.execute(True) -- -- return _zone -- -- def __unregister_port(self, _obj, port_id): -- if port_id in _obj.settings["ports"]: -- del _obj.settings["ports"][port_id] -+ zone = self._fw.check_zone(zone) -+ p_name = self.policy_name_from_zones(zone, "HOST") -+ return self._fw.policy.list_services(p_name) -+ -+ def add_port(self, zone, port, protocol, timeout=0, sender=None): -+ zone = self._fw.check_zone(zone) -+ p_name = self.policy_name_from_zones(zone, "HOST") -+ self._fw.policy.add_port(p_name, port, protocol, timeout, sender) -+ return zone -+ -+ def remove_port(self, zone, port, protocol): -+ zone = self._fw.check_zone(zone) -+ p_name = self.policy_name_from_zones(zone, "HOST") -+ self._fw.policy.remove_port(p_name, port, protocol) -+ return zone - - def query_port(self, zone, port, protocol): -- for (_port, _protocol) in self.get_settings(zone)["ports"]: -- if portInPortRange(port, _port) and protocol == _protocol: -- return True -- -- return False -+ zone = self._fw.check_zone(zone) -+ p_name = self.policy_name_from_zones(zone, "HOST") -+ return self._fw.policy.query_port(p_name, port, protocol) - - def list_ports(self, zone): -- return list(self.get_settings(zone)["ports"].keys()) -- -- # PROTOCOLS -- -- def check_protocol(self, protocol): -- if not checkProtocol(protocol): -- raise FirewallError(errors.INVALID_PROTOCOL, protocol) -- -- def __protocol_id(self, protocol): -- self.check_protocol(protocol) -- return protocol -- -- def add_protocol(self, zone, protocol, timeout=0, sender=None, -- use_transaction=None): -- _zone = self._fw.check_zone(zone) -- self._fw.check_timeout(timeout) -- self._fw.check_panic() -- _obj = self._zones[_zone] -- -- protocol_id = self.__protocol_id(protocol) -- if protocol_id in _obj.settings["protocols"]: -- raise FirewallError(errors.ALREADY_ENABLED, -- "'%s' already in '%s'" % (protocol, _zone)) -- -- if use_transaction is None: -- transaction = self.new_transaction() -- else: -- transaction = use_transaction -+ zone = self._fw.check_zone(zone) -+ p_name = self.policy_name_from_zones(zone, "HOST") -+ return self._fw.policy.list_ports(p_name) -+ -+ def add_source_port(self, zone, source_port, protocol, timeout=0, sender=None): -+ zone = self._fw.check_zone(zone) -+ p_name = self.policy_name_from_zones(zone, "HOST") -+ self._fw.policy.add_source_port(p_name, source_port, protocol, timeout, sender) -+ return zone -+ -+ def remove_source_port(self, zone, source_port, protocol): -+ zone = self._fw.check_zone(zone) -+ p_name = self.policy_name_from_zones(zone, "HOST") -+ self._fw.policy.remove_source_port(p_name, source_port, protocol) -+ return zone -+ -+ def query_source_port(self, zone, source_port, protocol): -+ zone = self._fw.check_zone(zone) -+ p_name = self.policy_name_from_zones(zone, "HOST") -+ return self._fw.policy.query_source_port(p_name, source_port, protocol) - -- if _obj.applied: -- self._protocol(True, _zone, protocol, transaction) -- -- self.__register_protocol(_obj, protocol_id, timeout, sender) -- transaction.add_fail(self.__unregister_protocol, _obj, protocol_id) -+ def list_source_ports(self, zone): -+ zone = self._fw.check_zone(zone) -+ p_name = self.policy_name_from_zones(zone, "HOST") -+ return self._fw.policy.list_source_ports(p_name) -+ -+ def _rich_rule_to_policies(self, zone, rule): -+ zone = self._fw.check_zone(zone) -+ if type(rule.action) == Rich_Mark: -+ return [self.policy_name_from_zones(zone, "ANY")] -+ elif type(rule.element) in [Rich_Service, Rich_Port, Rich_Protocol, -+ Rich_SourcePort]: -+ return [self.policy_name_from_zones(zone, "HOST")] -+ elif type(rule.element) in [Rich_IcmpBlock, Rich_IcmpType]: -+ return [self.policy_name_from_zones(zone, "HOST"), -+ self.policy_name_from_zones(zone, "ANY")] -+ elif type(rule.element) in [Rich_ForwardPort]: -+ return [self.policy_name_from_zones(zone, "ANY")] -+ elif type(rule.element) in [Rich_Masquerade]: -+ return [self.policy_name_from_zones("ANY", zone)] -+ elif rule.element is None: -+ return [self.policy_name_from_zones(zone, "HOST")] -+ else: -+ raise FirewallError("Rich rule type (%s) not handled." % (type(rule.element))) -+ -+ def add_rule(self, zone, rule, timeout=0, sender=None): -+ for p_name in self._rich_rule_to_policies(zone, rule): -+ self._fw.policy.add_rule(p_name, rule, timeout, sender) -+ return zone -+ -+ def remove_rule(self, zone, rule): -+ for p_name in self._rich_rule_to_policies(zone, rule): -+ self._fw.policy.remove_rule(p_name, rule) -+ return zone - -- if use_transaction is None: -- transaction.execute(True) -+ def query_rule(self, zone, rule): -+ ret = True -+ for p_name in self._rich_rule_to_policies(zone, rule): -+ ret = ret and self._fw.policy.query_rule(p_name, rule) -+ return ret - -- return _zone -+ def list_rules(self, zone): -+ ret = set() -+ for p_name in [self.policy_name_from_zones(zone, "ANY"), -+ self.policy_name_from_zones(zone, "HOST"), -+ self.policy_name_from_zones("ANY", zone)]: -+ ret.update(set(self._fw.policy.list_rules(p_name))) -+ return list(ret) -+ -+ def add_protocol(self, zone, protocol, timeout=0, sender=None): -+ zone = self._fw.check_zone(zone) -+ p_name = self.policy_name_from_zones(zone, "HOST") -+ self._fw.policy.add_protocol(p_name, protocol, timeout, sender) -+ return zone -+ -+ def remove_protocol(self, zone, protocol): -+ zone = self._fw.check_zone(zone) -+ p_name = self.policy_name_from_zones(zone, "HOST") -+ self._fw.policy.remove_protocol(p_name, protocol) -+ return zone - -- def __register_protocol(self, _obj, protocol_id, timeout, sender): -- _obj.settings["protocols"][protocol_id] = \ -- self.__gen_settings(timeout, sender) -+ def query_protocol(self, zone, protocol): -+ zone = self._fw.check_zone(zone) -+ p_name = self.policy_name_from_zones(zone, "HOST") -+ return self._fw.policy.query_protocol(p_name, protocol) - -- def remove_protocol(self, zone, protocol, -- use_transaction=None): -- _zone = self._fw.check_zone(zone) -- self._fw.check_panic() -- _obj = self._zones[_zone] -+ def list_protocols(self, zone): -+ zone = self._fw.check_zone(zone) -+ p_name = self.policy_name_from_zones(zone, "HOST") -+ return self._fw.policy.list_protocols(p_name) -+ -+ def add_masquerade(self, zone, timeout=0, sender=None): -+ zone = self._fw.check_zone(zone) -+ p_name = self.policy_name_from_zones("ANY", zone) -+ self._fw.policy.add_masquerade(p_name, timeout, sender) -+ return zone -+ -+ def remove_masquerade(self, zone): -+ zone = self._fw.check_zone(zone) -+ p_name = self.policy_name_from_zones("ANY", zone) -+ self._fw.policy.remove_masquerade(p_name) -+ return zone - -- protocol_id = self.__protocol_id(protocol) -- if protocol_id not in _obj.settings["protocols"]: -- raise FirewallError(errors.NOT_ENABLED, -- "'%s' not in '%s'" % (protocol, _zone)) -+ def query_masquerade(self, zone): -+ zone = self._fw.check_zone(zone) -+ p_name = self.policy_name_from_zones("ANY", zone) -+ return self._fw.policy.query_masquerade(p_name) - -- if use_transaction is None: -- transaction = self.new_transaction() -- else: -- transaction = use_transaction -+ def add_forward_port(self, zone, port, protocol, toport=None, -+ toaddr=None, timeout=0, sender=None): -+ zone = self._fw.check_zone(zone) -+ p_name = self.policy_name_from_zones(zone, "ANY") -+ self._fw.policy.add_forward_port(p_name, port, protocol, toport, toaddr, -+ timeout, sender) -+ return zone - -- if _obj.applied: -- self._protocol(False, _zone, protocol, transaction) -+ def remove_forward_port(self, zone, port, protocol, toport=None, -+ toaddr=None): -+ zone = self._fw.check_zone(zone) -+ p_name = self.policy_name_from_zones(zone, "ANY") -+ self._fw.policy.remove_forward_port(p_name, port, protocol, toport, toaddr) -+ return zone - -- transaction.add_post(self.__unregister_protocol, _obj, -- protocol_id) -+ def query_forward_port(self, zone, port, protocol, toport=None, -+ toaddr=None): -+ zone = self._fw.check_zone(zone) -+ p_name = self.policy_name_from_zones(zone, "ANY") -+ return self._fw.policy.query_forward_port(p_name, port, protocol, toport, -+ toaddr) - -- if use_transaction is None: -- transaction.execute(True) -+ def list_forward_ports(self, zone): -+ zone = self._fw.check_zone(zone) -+ p_name = self.policy_name_from_zones(zone, "ANY") -+ return self._fw.policy.list_forward_ports(p_name) - -- return _zone -+ def add_icmp_block(self, zone, icmp, timeout=0, sender=None): -+ zone = self._fw.check_zone(zone) -+ p_name = self.policy_name_from_zones(zone, "HOST") -+ self._fw.policy.add_icmp_block(p_name, icmp, timeout, sender) - -- def __unregister_protocol(self, _obj, protocol_id): -- if protocol_id in _obj.settings["protocols"]: -- del _obj.settings["protocols"][protocol_id] -+ p_name = self.policy_name_from_zones(zone, "ANY") -+ self._fw.policy.add_icmp_block(p_name, icmp, timeout, sender) -+ return zone - -- def query_protocol(self, zone, protocol): -- return self.__protocol_id(protocol) in self.get_settings(zone)["protocols"] -+ def remove_icmp_block(self, zone, icmp): -+ zone = self._fw.check_zone(zone) -+ p_name = self.policy_name_from_zones(zone, "HOST") -+ self._fw.policy.remove_icmp_block(p_name, icmp) - -- def list_protocols(self, zone): -- return list(self.get_settings(zone)["protocols"].keys()) -+ p_name = self.policy_name_from_zones(zone, "ANY") -+ self._fw.policy.remove_icmp_block(p_name, icmp) -+ return zone - -- # SOURCE PORTS -+ def query_icmp_block(self, zone, icmp): -+ zone = self._fw.check_zone(zone) -+ p_name_host = self.policy_name_from_zones(zone, "HOST") -+ p_name_fwd = self.policy_name_from_zones(zone, "ANY") -+ return self._fw.policy.query_icmp_block(p_name_host, icmp) and \ -+ self._fw.policy.query_icmp_block(p_name_fwd, icmp) - -- def __source_port_id(self, port, protocol): -- self.check_port(port, protocol) -- return (portStr(port, "-"), protocol) -+ def list_icmp_blocks(self, zone): -+ zone = self._fw.check_zone(zone) -+ p_name_host = self.policy_name_from_zones(zone, "HOST") -+ p_name_fwd = self.policy_name_from_zones(zone, "ANY") -+ return sorted(set(self._fw.policy.list_icmp_blocks(p_name_host) + -+ self._fw.policy.list_icmp_blocks(p_name_fwd))) - -- def add_source_port(self, zone, port, protocol, timeout=0, sender=None, -- use_transaction=None): -- _zone = self._fw.check_zone(zone) -- self._fw.check_timeout(timeout) -- self._fw.check_panic() -- _obj = self._zones[_zone] -+ def add_icmp_block_inversion(self, zone, sender=None): -+ zone = self._fw.check_zone(zone) -+ p_name = self.policy_name_from_zones(zone, "HOST") -+ self._fw.policy.add_icmp_block_inversion(p_name, sender) - -- existing_port_ids = list(filter(lambda x: x[1] == protocol, _obj.settings["source_ports"])) -- for port_id in existing_port_ids: -- if portInPortRange(port, port_id[0]): -- raise FirewallError(errors.ALREADY_ENABLED, -- "'%s:%s' already in '%s'" % (port, protocol, _zone)) -+ p_name = self.policy_name_from_zones(zone, "ANY") -+ self._fw.policy.add_icmp_block_inversion(p_name, sender) -+ return zone - -- added_ranges, removed_ranges = coalescePortRange(port, [_port for (_port, _protocol) in existing_port_ids]) -+ def _icmp_block_inversion(self, enable, zone, transaction): -+ zone = self._fw.check_zone(zone) -+ p_name = self.policy_name_from_zones(zone, "HOST") -+ self._fw.policy._icmp_block_inversion(enable, p_name, transaction) - -- if use_transaction is None: -- transaction = self.new_transaction() -- else: -- transaction = use_transaction -+ p_name = self.policy_name_from_zones(zone, "ANY") -+ self._fw.policy._icmp_block_inversion(enable, p_name, transaction) - -- if _obj.applied: -- for range in added_ranges: -- self._source_port(True, _zone, portStr(range, "-"), protocol, transaction) -- for range in removed_ranges: -- self._source_port(False, _zone, portStr(range, "-"), protocol, transaction) -+ def remove_icmp_block_inversion(self, zone): -+ zone = self._fw.check_zone(zone) -+ p_name = self.policy_name_from_zones(zone, "HOST") -+ self._fw.policy.remove_icmp_block_inversion(p_name) - -- for range in added_ranges: -- port_id = self.__source_port_id(range, protocol) -- self.__register_source_port(_obj, port_id, timeout, sender) -- transaction.add_fail(self.__unregister_source_port, _obj, port_id) -- for range in removed_ranges: -- port_id = self.__source_port_id(range, protocol) -- transaction.add_post(self.__unregister_source_port, _obj, port_id) -- -- if use_transaction is None: -- transaction.execute(True) -- -- return _zone -- -- def __register_source_port(self, _obj, port_id, timeout, sender): -- _obj.settings["source_ports"][port_id] = \ -- self.__gen_settings(timeout, sender) -- -- def remove_source_port(self, zone, port, protocol, -- use_transaction=None): -- _zone = self._fw.check_zone(zone) -- self._fw.check_panic() -- _obj = self._zones[_zone] -- -- existing_port_ids = list(filter(lambda x: x[1] == protocol, _obj.settings["source_ports"])) -- for port_id in existing_port_ids: -- if portInPortRange(port, port_id[0]): -- break -- else: -- raise FirewallError(errors.NOT_ENABLED, -- "'%s:%s' not in '%s'" % (port, protocol, _zone)) -- -- added_ranges, removed_ranges = breakPortRange(port, [_port for (_port, _protocol) in existing_port_ids]) -- -- if use_transaction is None: -- transaction = self.new_transaction() -- else: -- transaction = use_transaction -- -- if _obj.applied: -- for range in added_ranges: -- self._source_port(True, _zone, portStr(range, "-"), protocol, transaction) -- for range in removed_ranges: -- self._source_port(False, _zone, portStr(range, "-"), protocol, transaction) -- -- for range in added_ranges: -- port_id = self.__source_port_id(range, protocol) -- self.__register_source_port(_obj, port_id, 0, None) -- transaction.add_fail(self.__unregister_source_port, _obj, port_id) -- for range in removed_ranges: -- port_id = self.__source_port_id(range, protocol) -- transaction.add_post(self.__unregister_source_port, _obj, port_id) -- -- if use_transaction is None: -- transaction.execute(True) -- -- return _zone -- -- def __unregister_source_port(self, _obj, port_id): -- if port_id in _obj.settings["source_ports"]: -- del _obj.settings["source_ports"][port_id] -- -- def query_source_port(self, zone, port, protocol): -- for (_port, _protocol) in self.get_settings(zone)["source_ports"]: -- if portInPortRange(port, _port) and protocol == _protocol: -- return True -- -- return False -- -- def list_source_ports(self, zone): -- return list(self.get_settings(zone)["source_ports"].keys()) -- -- # MASQUERADE -- -- def __masquerade_id(self): -- return True -- -- def add_masquerade(self, zone, timeout=0, sender=None, -- use_transaction=None): -- _zone = self._fw.check_zone(zone) -- self._fw.check_timeout(timeout) -- self._fw.check_panic() -- _obj = self._zones[_zone] -- -- masquerade_id = self.__masquerade_id() -- if masquerade_id in _obj.settings["masquerade"]: -- raise FirewallError(errors.ALREADY_ENABLED, -- "masquerade already enabled in '%s'" % _zone) -- -- if use_transaction is None: -- transaction = self.new_transaction() -- else: -- transaction = use_transaction -- -- if _obj.applied: -- self._masquerade(True, _zone, transaction) -- -- self.__register_masquerade(_obj, masquerade_id, timeout, sender) -- transaction.add_fail(self.__unregister_masquerade, _obj, masquerade_id) -- -- if use_transaction is None: -- transaction.execute(True) -- -- return _zone -- -- def __register_masquerade(self, _obj, masquerade_id, timeout, sender): -- _obj.settings["masquerade"][masquerade_id] = \ -- self.__gen_settings(timeout, sender) -- -- def remove_masquerade(self, zone, use_transaction=None): -- _zone = self._fw.check_zone(zone) -- self._fw.check_panic() -- _obj = self._zones[_zone] -- -- masquerade_id = self.__masquerade_id() -- if masquerade_id not in _obj.settings["masquerade"]: -- raise FirewallError(errors.NOT_ENABLED, -- "masquerade not enabled in '%s'" % _zone) -- -- if use_transaction is None: -- transaction = self.new_transaction() -- else: -- transaction = use_transaction -- -- if _obj.applied: -- self._masquerade(False, _zone, transaction) -- -- transaction.add_post(self.__unregister_masquerade, _obj, masquerade_id) -- -- if use_transaction is None: -- transaction.execute(True) -- -- return _zone -- -- def __unregister_masquerade(self, _obj, masquerade_id): -- if masquerade_id in _obj.settings["masquerade"]: -- del _obj.settings["masquerade"][masquerade_id] -- -- def query_masquerade(self, zone): -- return self.__masquerade_id() in self.get_settings(zone)["masquerade"] -- -- # PORT FORWARDING -- -- def check_forward_port(self, ipv, port, protocol, toport=None, toaddr=None): -- self._fw.check_port(port) -- self._fw.check_tcpudp(protocol) -- if toport: -- self._fw.check_port(toport) -- if toaddr: -- if not check_single_address(ipv, toaddr): -- raise FirewallError(errors.INVALID_ADDR, toaddr) -- if not toport and not toaddr: -- raise FirewallError( -- errors.INVALID_FORWARD, -- "port-forwarding is missing to-port AND to-addr") -- -- def __forward_port_id(self, port, protocol, toport=None, toaddr=None): -- if check_single_address("ipv6", toaddr): -- self.check_forward_port("ipv6", port, protocol, toport, toaddr) -- else: -- self.check_forward_port("ipv4", port, protocol, toport, toaddr) -- return (portStr(port, "-"), protocol, -- portStr(toport, "-"), str(toaddr)) -- -- def add_forward_port(self, zone, port, protocol, toport=None, -- toaddr=None, timeout=0, sender=None, -- use_transaction=None): -- _zone = self._fw.check_zone(zone) -- self._fw.check_timeout(timeout) -- self._fw.check_panic() -- _obj = self._zones[_zone] -- -- forward_id = self.__forward_port_id(port, protocol, toport, toaddr) -- if forward_id in _obj.settings["forward_ports"]: -- raise FirewallError(errors.ALREADY_ENABLED, -- "'%s:%s:%s:%s' already in '%s'" % \ -- (port, protocol, toport, toaddr, _zone)) -- -- if use_transaction is None: -- transaction = self.new_transaction() -- else: -- transaction = use_transaction -- -- if _obj.applied: -- self._forward_port(True, _zone, transaction, port, protocol, -- toport, toaddr) -- -- self.__register_forward_port(_obj, forward_id, timeout, sender) -- transaction.add_fail(self.__unregister_forward_port, _obj, forward_id) -- -- if use_transaction is None: -- transaction.execute(True) -- -- return _zone -- -- def __register_forward_port(self, _obj, forward_id, timeout, sender): -- _obj.settings["forward_ports"][forward_id] = \ -- self.__gen_settings(timeout, sender) -- -- def remove_forward_port(self, zone, port, protocol, toport=None, -- toaddr=None, use_transaction=None): -- _zone = self._fw.check_zone(zone) -- self._fw.check_panic() -- _obj = self._zones[_zone] -- -- forward_id = self.__forward_port_id(port, protocol, toport, toaddr) -- if forward_id not in _obj.settings["forward_ports"]: -- raise FirewallError(errors.NOT_ENABLED, -- "'%s:%s:%s:%s' not in '%s'" % \ -- (port, protocol, toport, toaddr, _zone)) -- -- if use_transaction is None: -- transaction = self.new_transaction() -- else: -- transaction = use_transaction -- -- if _obj.applied: -- self._forward_port(False, _zone, transaction, port, protocol, -- toport, toaddr) -- -- transaction.add_post(self.__unregister_forward_port, _obj, forward_id) -- -- if use_transaction is None: -- transaction.execute(True) -- -- return _zone -- -- def __unregister_forward_port(self, _obj, forward_id): -- if forward_id in _obj.settings["forward_ports"]: -- del _obj.settings["forward_ports"][forward_id] -- -- def query_forward_port(self, zone, port, protocol, toport=None, -- toaddr=None): -- forward_id = self.__forward_port_id(port, protocol, toport, toaddr) -- return forward_id in self.get_settings(zone)["forward_ports"] -- -- def list_forward_ports(self, zone): -- return list(self.get_settings(zone)["forward_ports"].keys()) -- -- # ICMP BLOCK -- -- def check_icmp_block(self, icmp): -- self._fw.check_icmptype(icmp) -- -- def __icmp_block_id(self, icmp): -- self.check_icmp_block(icmp) -- return icmp -- -- def add_icmp_block(self, zone, icmp, timeout=0, sender=None, -- use_transaction=None): -- _zone = self._fw.check_zone(zone) -- self._fw.check_timeout(timeout) -- self._fw.check_panic() -- _obj = self._zones[_zone] -- -- icmp_id = self.__icmp_block_id(icmp) -- if icmp_id in _obj.settings["icmp_blocks"]: -- raise FirewallError(errors.ALREADY_ENABLED, -- "'%s' already in '%s'" % (icmp, _zone)) -- -- if use_transaction is None: -- transaction = self.new_transaction() -- else: -- transaction = use_transaction -- -- if _obj.applied: -- self._icmp_block(True, _zone, icmp, transaction) -- -- self.__register_icmp_block(_obj, icmp_id, timeout, sender) -- transaction.add_fail(self.__unregister_icmp_block, _obj, icmp_id) -- -- if use_transaction is None: -- transaction.execute(True) -- -- return _zone -- -- def __register_icmp_block(self, _obj, icmp_id, timeout, sender): -- _obj.settings["icmp_blocks"][icmp_id] = \ -- self.__gen_settings(timeout, sender) -- -- def remove_icmp_block(self, zone, icmp, use_transaction=None): -- _zone = self._fw.check_zone(zone) -- self._fw.check_panic() -- _obj = self._zones[_zone] -- -- icmp_id = self.__icmp_block_id(icmp) -- if icmp_id not in _obj.settings["icmp_blocks"]: -- raise FirewallError(errors.NOT_ENABLED, -- "'%s' not in '%s'" % (icmp, _zone)) -- -- if use_transaction is None: -- transaction = self.new_transaction() -- else: -- transaction = use_transaction -- -- if _obj.applied: -- self._icmp_block(False, _zone, icmp, transaction) -- -- transaction.add_post(self.__unregister_icmp_block, _obj, icmp_id) -- -- if use_transaction is None: -- transaction.execute(True) -- -- return _zone -- -- def __unregister_icmp_block(self, _obj, icmp_id): -- if icmp_id in _obj.settings["icmp_blocks"]: -- del _obj.settings["icmp_blocks"][icmp_id] -- -- def query_icmp_block(self, zone, icmp): -- return self.__icmp_block_id(icmp) in self.get_settings(zone)["icmp_blocks"] -- -- def list_icmp_blocks(self, zone): -- return self.get_settings(zone)["icmp_blocks"].keys() -- -- # ICMP BLOCK INVERSION -- -- def __icmp_block_inversion_id(self): -- return True -- -- def add_icmp_block_inversion(self, zone, sender=None, -- use_transaction=None): -- _zone = self._fw.check_zone(zone) -- self._fw.check_panic() -- _obj = self._zones[_zone] -- -- icmp_block_inversion_id = self.__icmp_block_inversion_id() -- if icmp_block_inversion_id in _obj.settings["icmp_block_inversion"]: -- raise FirewallError( -- errors.ALREADY_ENABLED, -- "icmp-block-inversion already enabled in '%s'" % _zone) -- -- if use_transaction is None: -- transaction = self.new_transaction() -- else: -- transaction = use_transaction -- -- if _obj.applied: -- # undo icmp blocks -- for args in self.get_settings(_zone)["icmp_blocks"]: -- self._icmp_block(False, _zone, args, transaction) -- -- self._icmp_block_inversion(False, _zone, transaction) -- -- self.__register_icmp_block_inversion(_obj, icmp_block_inversion_id, -- sender) -- transaction.add_fail(self.__undo_icmp_block_inversion, _zone, _obj, -- icmp_block_inversion_id) -- -- # redo icmp blocks -- if _obj.applied: -- for args in self.get_settings(_zone)["icmp_blocks"]: -- self._icmp_block(True, _zone, args, transaction) -- -- self._icmp_block_inversion(True, _zone, transaction) -- -- if use_transaction is None: -- transaction.execute(True) -- -- return _zone -- -- def __register_icmp_block_inversion(self, _obj, icmp_block_inversion_id, -- sender): -- _obj.settings["icmp_block_inversion"][icmp_block_inversion_id] = \ -- self.__gen_settings(0, sender) -- -- def __undo_icmp_block_inversion(self, _zone, _obj, icmp_block_inversion_id): -- transaction = self.new_transaction() -- -- # undo icmp blocks -- if _obj.applied: -- for args in self.get_settings(_zone)["icmp_blocks"]: -- self._icmp_block(False, _zone, args, transaction) -- -- if icmp_block_inversion_id in _obj.settings["icmp_block_inversion"]: -- del _obj.settings["icmp_block_inversion"][icmp_block_inversion_id] -- -- # redo icmp blocks -- if _obj.applied: -- for args in self.get_settings(_zone)["icmp_blocks"]: -- self._icmp_block(True, _zone, args, transaction) -- -- transaction.execute(True) -- -- def remove_icmp_block_inversion(self, zone, use_transaction=None): -- _zone = self._fw.check_zone(zone) -- self._fw.check_panic() -- _obj = self._zones[_zone] -- -- icmp_block_inversion_id = self.__icmp_block_inversion_id() -- if icmp_block_inversion_id not in _obj.settings["icmp_block_inversion"]: -- raise FirewallError( -- errors.NOT_ENABLED, -- "icmp-block-inversion not enabled in '%s'" % _zone) -- -- if use_transaction is None: -- transaction = self.new_transaction() -- else: -- transaction = use_transaction -- -- if _obj.applied: -- # undo icmp blocks -- for args in self.get_settings(_zone)["icmp_blocks"]: -- self._icmp_block(False, _zone, args, transaction) -- -- self._icmp_block_inversion(False, _zone, transaction) -- -- self.__unregister_icmp_block_inversion(_obj, -- icmp_block_inversion_id) -- transaction.add_fail(self.__register_icmp_block_inversion, _obj, -- icmp_block_inversion_id, None) -- -- # redo icmp blocks -- if _obj.applied: -- for args in self.get_settings(_zone)["icmp_blocks"]: -- self._icmp_block(True, _zone, args, transaction) -- -- self._icmp_block_inversion(True, _zone, transaction) -- -- if use_transaction is None: -- transaction.execute(True) -- -- return _zone -- -- def __unregister_icmp_block_inversion(self, _obj, icmp_block_inversion_id): -- if icmp_block_inversion_id in _obj.settings["icmp_block_inversion"]: -- del _obj.settings["icmp_block_inversion"][icmp_block_inversion_id] -+ p_name = self.policy_name_from_zones(zone, "ANY") -+ self._fw.policy.remove_icmp_block_inversion(p_name) -+ return zone - - def query_icmp_block_inversion(self, zone): -- return self.__icmp_block_inversion_id() in \ -- self.get_settings(zone)["icmp_block_inversion"] -- -- # dynamic chain handling -- -- def gen_chain_rules(self, zone, create, table, chain, transaction): -- if create: -- if zone in self._chains and \ -- table in self._chains[zone] and \ -- chain in self._chains[zone][table]: -- return -- else: -- if zone not in self._chains or \ -- table not in self._chains[zone] or \ -- chain not in self._chains[zone][table]: -- return -- -- for backend in self._fw.enabled_backends(): -- if backend.zones_supported and \ -- table in backend.get_available_tables(): -- rules = backend.build_zone_chain_rules(zone, table, chain) -- transaction.add_rules(backend, rules) -- -- self._register_chains(zone, create, [(table, chain)]) -- transaction.add_fail(self._register_chains, zone, create, [(table, chain)]) -- -- def _interface(self, enable, zone, interface, transaction, -- append=False): -- for backend in self._fw.enabled_backends(): -- if not backend.zones_supported: -- continue -- for table in backend.get_available_tables(): -- for chain in backend.get_zone_table_chains(table): -- # create needed chains if not done already -- if enable: -- transaction.add_chain(zone, table, chain) -- -- rules = backend.build_zone_source_interface_rules(enable, -- zone, interface, table, chain, append) -- transaction.add_rules(backend, rules) -- -- # IPSETS -- -- def _ipset_family(self, name): -- if self._fw.ipset.get_type(name) == "hash:mac": -- return None -- return self._fw.ipset.get_family(name) -- -- def __ipset_type(self, name): -- return self._fw.ipset.get_type(name) -- -- def _ipset_match_flags(self, name, flag): -- return ",".join([flag] * self._fw.ipset.get_dimension(name)) -- -- def _check_ipset_applied(self, name): -- return self._fw.ipset.check_applied(name) -- -- def _check_ipset_type_for_source(self, name): -- _type = self.__ipset_type(name) -- if _type not in ZONE_SOURCE_IPSET_TYPES: -- raise FirewallError( -- errors.INVALID_IPSET, -- "ipset '%s' with type '%s' not usable as source" % \ -- (name, _type)) -- -- def _source(self, enable, zone, ipv, source, transaction): -- # For mac source bindings ipv is an empty string, the mac source will -- # be added for ipv4 and ipv6 -- for backend in [self._fw.get_backend_by_ipv(ipv)] if ipv else self._fw.enabled_backends(): -- if not backend.zones_supported: -- continue -- for table in backend.get_available_tables(): -- for chain in backend.get_zone_table_chains(table): -- # create needed chains if not done already -- if enable: -- transaction.add_chain(zone, table, chain) -- -- rules = backend.build_zone_source_address_rules(enable, zone, -- source, table, chain) -- transaction.add_rules(backend, rules) -- -- def _rule_prepare(self, enable, zone, rule, transaction): -- if rule.family is not None: -- ipvs = [ rule.family ] -- else: -- ipvs = [ipv for ipv in ["ipv4", "ipv6"] if self._fw.is_ipv_enabled(ipv)] -- -- source_ipv = self._rule_source_ipv(rule.source) -- if source_ipv is not None and source_ipv != "": -- if rule.family is not None: -- # rule family is defined by user, no way to change it -- if rule.family != source_ipv: -- raise FirewallError(errors.INVALID_RULE, -- "Source address family '%s' conflicts with rule family '%s'." % (source_ipv, rule.family)) -- else: -- # use the source family as rule family -- ipvs = [ source_ipv ] -- -- # add an element to object to allow backends to know what ipvs this applies to -- rule.ipvs = ipvs -- -- for backend in set([self._fw.get_backend_by_ipv(x) for x in ipvs]): -- # SERVICE -- if type(rule.element) == Rich_Service: -- svc = self._fw.service.get_service(rule.element.name) -- -- destinations = [] -- if len(svc.destination) > 0: -- if rule.destination: -- # we can not use two destinations at the same time -- raise FirewallError(errors.INVALID_RULE, -- "Destination conflict with service.") -- for ipv in ipvs: -- if ipv in svc.destination and backend.is_ipv_supported(ipv): -- destinations.append(svc.destination[ipv]) -- else: -- # dummy for the following for loop -- destinations.append(None) -- -- for destination in destinations: -- if enable: -- transaction.add_chain(zone, "filter", "INPUT") -- transaction.add_chain(zone, "raw", "PREROUTING") -- -- if type(rule.action) == Rich_Accept: -- # only load modules for accept action -- helpers = self.get_helpers_for_service_modules(svc.modules, -- enable) -- helpers += self.get_helpers_for_service_helpers(svc.helpers) -- helpers = sorted(set(helpers), key=lambda x: x.name) -- -- modules = [ ] -- for helper in helpers: -- module = helper.module -- _module_short_name = get_nf_conntrack_short_name(module) -- nat_module = module.replace("conntrack", "nat") -- modules.append(nat_module) -- if helper.family != "" and not backend.is_ipv_supported(helper.family): -- # no support for family ipv, continue -- continue -- if len(helper.ports) < 1: -- modules.append(module) -- else: -- for (port,proto) in helper.ports: -- rules = backend.build_zone_helper_ports_rules( -- enable, zone, proto, port, -- destination, helper.name, _module_short_name) -- transaction.add_rules(backend, rules) -- transaction.add_modules(modules) -- -- # create rules -- for (port,proto) in svc.ports: -- if enable and type(rule.action) == Rich_Mark: -- transaction.add_chain(zone, "mangle", "PREROUTING") -- rules = backend.build_zone_ports_rules( -- enable, zone, proto, port, destination, rule) -- transaction.add_rules(backend, rules) -- -- for proto in svc.protocols: -- if enable and type(rule.action) == Rich_Mark: -- transaction.add_chain(zone, "mangle", "PREROUTING") -- rules = backend.build_zone_protocol_rules( -- enable, zone, proto, destination, rule) -- transaction.add_rules(backend, rules) -- -- # create rules -- for (port,proto) in svc.source_ports: -- if enable and type(rule.action) == Rich_Mark: -- transaction.add_chain(zone, "mangle", "PREROUTING") -- rules = backend.build_zone_source_ports_rules( -- enable, zone, proto, port, destination, rule) -- transaction.add_rules(backend, rules) -- -- # PORT -- elif type(rule.element) == Rich_Port: -- port = rule.element.port -- protocol = rule.element.protocol -- self.check_port(port, protocol) -- -- if enable: -- transaction.add_chain(zone, "filter", "INPUT") -- if enable and type(rule.action) == Rich_Mark: -- transaction.add_chain(zone, "mangle", "PREROUTING") -- -- rules = backend.build_zone_ports_rules( -- enable, zone, protocol, port, None, rule) -- transaction.add_rules(backend, rules) -- -- # PROTOCOL -- elif type(rule.element) == Rich_Protocol: -- protocol = rule.element.value -- self.check_protocol(protocol) -- -- if enable: -- transaction.add_chain(zone, "filter", "INPUT") -- if enable and type(rule.action) == Rich_Mark: -- transaction.add_chain(zone, "mangle", "PREROUTING") -- -- rules = backend.build_zone_protocol_rules( -- enable, zone, protocol, None, rule) -- transaction.add_rules(backend, rules) -- -- # MASQUERADE -- elif type(rule.element) == Rich_Masquerade: -- if enable: -- transaction.add_chain(zone, "nat", "POSTROUTING") -- transaction.add_chain(zone, "filter", "FORWARD_OUT") -- for ipv in ipvs: -- if backend.is_ipv_supported(ipv): -- transaction.add_post(enable_ip_forwarding, ipv) -- -- rules = backend.build_zone_masquerade_rules(enable, zone, rule) -- transaction.add_rules(backend, rules) -- -- # FORWARD PORT -- elif type(rule.element) == Rich_ForwardPort: -- port = rule.element.port -- protocol = rule.element.protocol -- toport = rule.element.to_port -- toaddr = rule.element.to_address -- for ipv in ipvs: -- if backend.is_ipv_supported(ipv): -- self.check_forward_port(ipv, port, protocol, toport, toaddr) -- if toaddr and enable: -- transaction.add_post(enable_ip_forwarding, ipv) -- -- if enable: -- transaction.add_chain(zone, "nat", "PREROUTING") -- -- rules = backend.build_zone_forward_port_rules( -- enable, zone, port, protocol, toport, -- toaddr, rule) -- transaction.add_rules(backend, rules) -- -- # SOURCE PORT -- elif type(rule.element) == Rich_SourcePort: -- port = rule.element.port -- protocol = rule.element.protocol -- self.check_port(port, protocol) -- -- if enable: -- transaction.add_chain(zone, "filter", "INPUT") -- if enable and type(rule.action) == Rich_Mark: -- transaction.add_chain(zone, "mangle", "PREROUTING") -- -- rules = backend.build_zone_source_ports_rules( -- enable, zone, protocol, port, None, rule) -- transaction.add_rules(backend, rules) -- -- # ICMP BLOCK and ICMP TYPE -- elif type(rule.element) == Rich_IcmpBlock or \ -- type(rule.element) == Rich_IcmpType: -- ict = self._fw.icmptype.get_icmptype(rule.element.name) -- -- if type(rule.element) == Rich_IcmpBlock and \ -- rule.action and type(rule.action) == Rich_Accept: -- # icmp block might have reject or drop action, but not accept -- raise FirewallError(errors.INVALID_RULE, -- "IcmpBlock not usable with accept action") -- if ict.destination: -- for ipv in ipvs: -- if ipv in ict.destination \ -- and not backend.is_ipv_supported(ipv): -- raise FirewallError( -- errors.INVALID_RULE, -- "Icmp%s %s not usable with %s" % \ -- ("Block" if type(rule.element) == \ -- Rich_IcmpBlock else "Type", -- rule.element.name, backend.name)) -- -- table = "filter" -- if enable: -- transaction.add_chain(zone, table, "INPUT") -- transaction.add_chain(zone, table, "FORWARD_IN") -- -- rules = backend.build_zone_icmp_block_rules(enable, zone, ict, rule) -- transaction.add_rules(backend, rules) -- -- elif rule.element is None: -- if enable: -- transaction.add_chain(zone, "filter", "INPUT") -- if enable and type(rule.action) == Rich_Mark: -- transaction.add_chain(zone, "mangle", "PREROUTING") -- -- rules = backend.build_zone_rich_source_destination_rules( -- enable, zone, rule) -- transaction.add_rules(backend, rules) -- -- # EVERYTHING ELSE -- else: -- raise FirewallError(errors.INVALID_RULE, "Unknown element %s" % -- type(rule.element)) -- -- def _service(self, enable, zone, service, transaction, included_services=None): -- svc = self._fw.service.get_service(service) -- helpers = self.get_helpers_for_service_modules(svc.modules, enable) -- helpers += self.get_helpers_for_service_helpers(svc.helpers) -- helpers = sorted(set(helpers), key=lambda x: x.name) -- -- # First apply any services this service may include -- if included_services is None: -- included_services = [service] -- for include in svc.includes: -- if include in included_services: -- continue -- self.check_service(include) -- included_services.append(include) -- self._service(enable, zone, include, transaction, included_services=included_services) -- -- if enable: -- transaction.add_chain(zone, "raw", "PREROUTING") -- transaction.add_chain(zone, "filter", "INPUT") -- -- # build a list of (backend, destination). The destination may be ipv4, -- # ipv6 or None -- # -- backends_ipv = [] -- for ipv in ["ipv4", "ipv6"]: -- if not self._fw.is_ipv_enabled(ipv): -- continue -- backend = self._fw.get_backend_by_ipv(ipv) -- if len(svc.destination) > 0: -- if ipv in svc.destination: -- backends_ipv.append((backend, svc.destination[ipv])) -- else: -- if (backend, None) not in backends_ipv: -- backends_ipv.append((backend, None)) -- -- for (backend,destination) in backends_ipv: -- for helper in helpers: -- module = helper.module -- _module_short_name = get_nf_conntrack_short_name(module) -- nat_module = helper.module.replace("conntrack", "nat") -- transaction.add_module(nat_module) -- if helper.family != "" and not backend.is_ipv_supported(helper.family): -- # no support for family ipv, continue -- continue -- if len(helper.ports) < 1: -- transaction.add_module(module) -- else: -- for (port,proto) in helper.ports: -- rules = backend.build_zone_helper_ports_rules( -- enable, zone, proto, port, -- destination, helper.name, _module_short_name) -- transaction.add_rules(backend, rules) -- -- for (port,proto) in svc.ports: -- rules = backend.build_zone_ports_rules(enable, zone, proto, -- port, destination) -- transaction.add_rules(backend, rules) -- -- for protocol in svc.protocols: -- rules = backend.build_zone_protocol_rules( -- enable, zone, protocol, destination) -- transaction.add_rules(backend, rules) -- -- for (port,proto) in svc.source_ports: -- rules = backend.build_zone_source_ports_rules( -- enable, zone, proto, port, destination) -- transaction.add_rules(backend, rules) -- -- def _port(self, enable, zone, port, protocol, transaction): -- if enable: -- transaction.add_chain(zone, "filter", "INPUT") -- -- for backend in self._fw.enabled_backends(): -- if not backend.zones_supported: -- continue -- -- rules = backend.build_zone_ports_rules(enable, zone, protocol, -- port) -- transaction.add_rules(backend, rules) -- -- def _protocol(self, enable, zone, protocol, transaction): -- if enable: -- transaction.add_chain(zone, "filter", "INPUT") -- -- for backend in self._fw.enabled_backends(): -- if not backend.zones_supported: -- continue -- -- rules = backend.build_zone_protocol_rules(enable, zone, protocol) -- transaction.add_rules(backend, rules) -- -- def _source_port(self, enable, zone, port, protocol, transaction): -- if enable: -- transaction.add_chain(zone, "filter", "INPUT") -- -- for backend in self._fw.enabled_backends(): -- if not backend.zones_supported: -- continue -- -- rules = backend.build_zone_source_ports_rules(enable, zone, protocol, port) -- transaction.add_rules(backend, rules) -- -- def _masquerade(self, enable, zone, transaction): -- if enable: -- transaction.add_chain(zone, "nat", "POSTROUTING") -- transaction.add_chain(zone, "filter", "FORWARD_OUT") -- -- ipv = "ipv4" -- transaction.add_post(enable_ip_forwarding, ipv) -- -- backend = self._fw.get_backend_by_ipv(ipv) -- rules = backend.build_zone_masquerade_rules(enable, zone) -- transaction.add_rules(backend, rules) -- -- def _forward_port(self, enable, zone, transaction, port, protocol, -- toport=None, toaddr=None): -- if check_single_address("ipv6", toaddr): -- ipv = "ipv6" -- else: -- ipv = "ipv4" -- -- if enable: -- transaction.add_chain(zone, "nat", "PREROUTING") -- -- if toaddr and enable: -- transaction.add_post(enable_ip_forwarding, ipv) -- backend = self._fw.get_backend_by_ipv(ipv) -- rules = backend.build_zone_forward_port_rules( -- enable, zone, port, protocol, toport, -- toaddr) -- transaction.add_rules(backend, rules) -- -- def _icmp_block(self, enable, zone, icmp, transaction): -- ict = self._fw.icmptype.get_icmptype(icmp) -- -- if enable: -- transaction.add_chain(zone, "filter", "INPUT") -- transaction.add_chain(zone, "filter", "FORWARD_IN") -- -- for backend in self._fw.enabled_backends(): -- if not backend.zones_supported: -- continue -- skip_backend = False -- -- if ict.destination: -- for ipv in ["ipv4", "ipv6"]: -- if ipv in ict.destination: -- if not backend.is_ipv_supported(ipv): -- skip_backend = True -- break -- -- if skip_backend: -- continue -- -- rules = backend.build_zone_icmp_block_rules(enable, zone, ict) -- transaction.add_rules(backend, rules) -- -- def _icmp_block_inversion(self, enable, zone, transaction): -- target = self._zones[zone].target -- -- # Do not add general icmp accept rules into a trusted, block or drop -- # zone. -- if target in [ "DROP", "%%REJECT%%", "REJECT" ]: -- return -- if not self.query_icmp_block_inversion(zone) and target == "ACCEPT": -- # ibi target and zone target are ACCEPT, no need to add an extra -- # rule -- return -- -- transaction.add_chain(zone, "filter", "INPUT") -- transaction.add_chain(zone, "filter", "FORWARD_IN") -- -- for backend in self._fw.enabled_backends(): -- if not backend.zones_supported: -- continue -- -- rules = backend.build_zone_icmp_block_inversion_rules(enable, zone) -- transaction.add_rules(backend, rules) -+ zone = self._fw.check_zone(zone) -+ p_name_host = self.policy_name_from_zones(zone, "HOST") -+ p_name_fwd = self.policy_name_from_zones(zone, "ANY") -+ return self._fw.policy.query_icmp_block_inversion(p_name_host) and \ -+ self._fw.policy.query_icmp_block_inversion(p_name_fwd) -diff --git a/src/firewall/core/io/policy.py b/src/firewall/core/io/policy.py -new file mode 100644 -index 0000000..c0171a6 ---- /dev/null -+++ b/src/firewall/core/io/policy.py -@@ -0,0 +1,830 @@ -+# -*- coding: utf-8 -*- -+# -+# SPDX-License-Identifier: GPL-2.0-or-later -+ -+__all__ = [ "Policy", "policy_reader", "policy_writer" ] -+ -+import xml.sax as sax -+import os -+import io -+import shutil -+import copy -+from collections import OrderedDict -+ -+from firewall import config -+from firewall.functions import checkIP, checkIP6 -+from firewall.functions import uniqify, max_policy_name_len, portStr -+from firewall.core.base import DEFAULT_POLICY_TARGET, POLICY_TARGETS -+from firewall.core.io.io_object import IO_Object, \ -+ IO_Object_ContentHandler, IO_Object_XMLGenerator, check_port, \ -+ check_tcpudp, check_protocol -+from firewall.core import rich -+from firewall.core.logger import log -+from firewall import errors -+from firewall.errors import FirewallError -+ -+class Policy(IO_Object): -+ priority_min = -32768 -+ priority_max = 32767 -+ priority_default = -1 -+ ADDITIONAL_ALNUM_CHARS = [ "_", "-", "/" ] -+ PARSER_REQUIRED_ELEMENT_ATTRS = { -+ "short": None, -+ "description": None, -+ "policy": ["target"], -+ "service": [ "name" ], -+ "port": [ "port", "protocol" ], -+ "icmp-block": [ "name" ], -+ "icmp-type": [ "name" ], -+ "forward-port": [ "port", "protocol" ], -+ "rule": None, -+ "destination": [ "address" ], -+ "protocol": [ "value" ], -+ "source-port": [ "port", "protocol" ], -+ "log": None, -+ "audit": None, -+ "accept": None, -+ "reject": None, -+ "drop": None, -+ "mark": [ "set" ], -+ "limit": [ "value" ], -+ "ingress-zone": None, -+ "egress-zone": None, -+ } -+ PARSER_OPTIONAL_ELEMENT_ATTRS = { -+ "policy": [ "version", "priority" ], -+ "forward-port": [ "to-port", "to-addr" ], -+ "rule": [ "family", "priority" ], -+ "destination": [ "invert" ], -+ "log": [ "prefix", "level" ], -+ "reject": [ "type" ], -+ } -+ -+ def __init__(self): -+ super(Policy, self).__init__() -+ self.version = "" -+ self.short = "" -+ self.description = "" -+ self.target = DEFAULT_POLICY_TARGET -+ self.services = [ ] -+ self.ports = [ ] -+ self.protocols = [ ] -+ self.icmp_blocks = [ ] -+ self.masquerade = False -+ self.forward_ports = [ ] -+ self.source_ports = [ ] -+ self.fw_config = None # to be able to check services and a icmp_blocks -+ self.rules = [ ] -+ self.combined = False -+ self.applied = False -+ self.priority = self.priority_default -+ self.derived_from_zone = None -+ self.ingress_zones = [] -+ self.egress_zones = [] -+ -+ def cleanup(self): -+ self.version = "" -+ self.short = "" -+ self.description = "" -+ self.target = DEFAULT_POLICY_TARGET -+ del self.services[:] -+ del self.ports[:] -+ del self.protocols[:] -+ del self.icmp_blocks[:] -+ self.masquerade = False -+ del self.forward_ports[:] -+ del self.source_ports[:] -+ self.fw_config = None # to be able to check services and a icmp_blocks -+ del self.rules[:] -+ self.combined = False -+ self.applied = False -+ self.priority = self.priority_default -+ del self.ingress_zones[:] -+ del self.egress_zones[:] -+ -+ def __getattr__(self, name): -+ if name == "rules_str": -+ rules_str = [str(rule) for rule in self.rules] -+ return rules_str -+ else: -+ return getattr(super(Policy, self), name) -+ -+ def __setattr__(self, name, value): -+ if name == "rules_str": -+ self.rules = [rich.Rich_Rule(rule_str=s) for s in value] -+ else: -+ super(Policy, self).__setattr__(name, value) -+ -+ def _check_config(self, config, item): -+ if item == "services" and self.fw_config: -+ existing_services = self.fw_config.get_services() -+ for service in config: -+ if service not in existing_services: -+ raise FirewallError(errors.INVALID_SERVICE, -+ "'%s' not among existing services" % \ -+ service) -+ elif item == "ports": -+ for port in config: -+ check_port(port[0]) -+ check_tcpudp(port[1]) -+ elif item == "protocols": -+ for proto in config: -+ check_protocol(proto) -+ elif item == "icmp_blocks" and self.fw_config: -+ existing_icmptypes = self.fw_config.get_icmptypes() -+ for icmptype in config: -+ if icmptype not in existing_icmptypes: -+ raise FirewallError(errors.INVALID_ICMPTYPE, -+ "'%s' not among existing icmp types" % \ -+ icmptype) -+ elif item == "forward_ports": -+ for fwd_port in config: -+ check_port(fwd_port[0]) -+ check_tcpudp(fwd_port[1]) -+ if not fwd_port[2] and not fwd_port[3]: -+ raise FirewallError( -+ errors.INVALID_FORWARD, -+ "'%s' is missing to-port AND to-addr " % fwd_port) -+ if fwd_port[2]: -+ check_port(fwd_port[2]) -+ if fwd_port[3]: -+ if not checkIP(fwd_port[3]) and not checkIP6(fwd_port[3]): -+ raise FirewallError( -+ errors.INVALID_ADDR, -+ "to-addr '%s' is not a valid address" % fwd_port[3]) -+ elif item == "source_ports": -+ for port in config: -+ check_port(port[0]) -+ check_tcpudp(port[1]) -+ elif item == "target": -+ if config not in POLICY_TARGETS: -+ raise FirewallError(errors.INVALID_TARGET, config) -+ elif item == "rules_str": -+ for rule in config: -+ rich.Rich_Rule(rule_str=rule) -+ elif item in ["ingress-zone", "egress-zone"] and self.fw_config: -+ existing_zones = self.fw_config.get_zones() -+ for zone in config: -+ if zone not in existing_zones: -+ raise FirewallError(errors.INVALID_SERVICE, -+ "'%s' not among existing zones" % zone) -+ -+ def check_name(self, name): -+ super(Policy, self).check_name(name) -+ if name.startswith('/'): -+ raise FirewallError(errors.INVALID_NAME, -+ "'%s' can't start with '/'" % name) -+ elif name.endswith('/'): -+ raise FirewallError(errors.INVALID_NAME, -+ "'%s' can't end with '/'" % name) -+ elif name.count('/') > 1: -+ raise FirewallError(errors.INVALID_NAME, -+ "more than one '/' in '%s'" % name) -+ else: -+ if "/" in name: -+ checked_name = name[:name.find('/')] -+ else: -+ checked_name = name -+ if len(checked_name) > max_policy_name_len(): -+ raise FirewallError(errors.INVALID_NAME, -+ "Policy of '%s' has %d chars, max is %d %s" % ( -+ name, len(checked_name), -+ max_policy_name_len(), -+ self.combined)) -+ -+ def import_config(self, conf): -+ self.check_config(conf) -+ -+ # FIXME: -+ for key in conf: -+ if not hasattr(self, key): -+ raise FirewallError(errors.UNKNOWN_ERROR, "Internal error. '{}' is not a valid attribute".format(key)) -+ if isinstance(conf[key], list): -+ # maintain list order while removing duplicates -+ setattr(self, key, list(OrderedDict.fromkeys(copy.deepcopy(conf[key])))) -+ else: -+ setattr(self, key, copy.deepcopy(conf[key])) -+ -+ def export_config(self): -+ conf = {} -+ # FIXME -+ type_formats = dict([(x[0], x[1]) for x in self.IMPORT_EXPORT_STRUCTURE]) -+ for key in type_formats: -+ if getattr(self, key): -+ conf[key] = copy.deepcopy(getattr(self, key)) -+ return conf -+ -+ def check_config(self, conf): -+ # FIXME -+ type_formats = dict([(x[0], x[1]) for x in self.IMPORT_EXPORT_STRUCTURE]) -+ for key in conf: -+ if key not in [x for (x,y) in self.IMPORT_EXPORT_STRUCTURE]: -+ raise FirewallError(errors.INVALID_OPTION, "policy option '{}' is not valid".format(key)) -+ self._check_config_structure(conf[key], type_formats[key]) -+ self._check_config(conf[key], key) -+ -+# PARSER -+ -+class policy_ContentHandler(IO_Object_ContentHandler): -+ def __init__(self, item): -+ IO_Object_ContentHandler.__init__(self, item) -+ self._rule = None -+ self._rule_error = False -+ self._limit_ok = None -+ -+ def startElement(self, name, attrs): -+ IO_Object_ContentHandler.startElement(self, name, attrs) -+ if self._rule_error: -+ return -+ -+ self.item.parser_check_element_attrs(name, attrs) -+ -+ if name == "policy": -+ if "version" in attrs: -+ self.item.version = attrs["version"] -+ if "priority" in attrs: -+ self.item.priority = int(attrs["priority"]) -+ target = attrs["target"] -+ if target not in POLICY_TARGETS: -+ raise FirewallError(errors.INVALID_TARGET, target) -+ -+ elif name == "short": -+ pass -+ elif name == "description": -+ pass -+ elif name == "service": -+ if self._rule: -+ if self._rule.element: -+ log.warning("Invalid rule: More than one element in rule '%s', ignoring.", -+ str(self._rule)) -+ self._rule_error = True -+ return -+ self._rule.element = rich.Rich_Service(attrs["name"]) -+ return -+ if attrs["name"] not in self.item.services: -+ self.item.services.append(attrs["name"]) -+ else: -+ log.warning("Service '%s' already set, ignoring.", -+ attrs["name"]) -+ elif name == "ingress-zone": -+ if attrs["name"] not in self.item.ingress_zones: -+ self.item.ingress_zones.append(attrs["name"]) -+ else: -+ log.warning("Ingress zone '%s' already set, ignoring.", attrs["name"]) -+ elif name == "egress-zone": -+ if attrs["name"] not in self.item.egress_zones: -+ self.item.egress_zones.append(attrs["name"]) -+ else: -+ log.warning("Egress zone '%s' already set, ignoring.", attrs["name"]) -+ -+ elif name == "port": -+ if self._rule: -+ if self._rule.element: -+ log.warning("Invalid rule: More than one element in rule '%s', ignoring.", -+ str(self._rule)) -+ self._rule_error = True -+ return -+ self._rule.element = rich.Rich_Port(attrs["port"], -+ attrs["protocol"]) -+ return -+ check_port(attrs["port"]) -+ check_tcpudp(attrs["protocol"]) -+ entry = (portStr(attrs["port"], "-"), attrs["protocol"]) -+ if entry not in self.item.ports: -+ self.item.ports.append(entry) -+ else: -+ log.warning("Port '%s/%s' already set, ignoring.", -+ attrs["port"], attrs["protocol"]) -+ -+ elif name == "protocol": -+ if self._rule: -+ if self._rule.element: -+ log.warning("Invalid rule: More than one element in rule '%s', ignoring.", -+ str(self._rule)) -+ self._rule_error = True -+ return -+ self._rule.element = rich.Rich_Protocol(attrs["value"]) -+ else: -+ check_protocol(attrs["value"]) -+ if attrs["value"] not in self.item.protocols: -+ self.item.protocols.append(attrs["value"]) -+ else: -+ log.warning("Protocol '%s' already set, ignoring.", -+ attrs["value"]) -+ elif name == "icmp-block": -+ if self._rule: -+ if self._rule.element: -+ log.warning("Invalid rule: More than one element in rule '%s', ignoring.", -+ str(self._rule)) -+ self._rule_error = True -+ return -+ self._rule.element = rich.Rich_IcmpBlock(attrs["name"]) -+ return -+ if attrs["name"] not in self.item.icmp_blocks: -+ self.item.icmp_blocks.append(attrs["name"]) -+ else: -+ log.warning("icmp-block '%s' already set, ignoring.", -+ attrs["name"]) -+ -+ elif name == "icmp-type": -+ if self._rule: -+ if self._rule.element: -+ log.warning("Invalid rule: More than one element in rule '%s', ignoring.", -+ str(self._rule)) -+ self._rule_error = True -+ return -+ self._rule.element = rich.Rich_IcmpType(attrs["name"]) -+ return -+ else: -+ log.warning("Invalid rule: icmp-block '%s' outside of rule", -+ attrs["name"]) -+ -+ elif name == "masquerade": -+ if self._rule: -+ if self._rule.element: -+ log.warning("Invalid rule: More than one element in rule '%s', ignoring.", -+ str(self._rule)) -+ self._rule_error = True -+ return -+ self._rule.element = rich.Rich_Masquerade() -+ else: -+ if self.item.masquerade: -+ log.warning("Masquerade already set, ignoring.") -+ else: -+ self.item.masquerade = True -+ -+ elif name == "forward-port": -+ to_port = "" -+ if "to-port" in attrs: -+ to_port = attrs["to-port"] -+ to_addr = "" -+ if "to-addr" in attrs: -+ to_addr = attrs["to-addr"] -+ -+ if self._rule: -+ if self._rule.element: -+ log.warning("Invalid rule: More than one element in rule '%s', ignoring.", -+ str(self._rule)) -+ self._rule_error = True -+ return -+ self._rule.element = rich.Rich_ForwardPort(attrs["port"], -+ attrs["protocol"], -+ to_port, to_addr) -+ return -+ -+ check_port(attrs["port"]) -+ check_tcpudp(attrs["protocol"]) -+ if to_port: -+ check_port(to_port) -+ if to_addr: -+ if not checkIP(to_addr) and not checkIP6(to_addr): -+ raise FirewallError(errors.INVALID_ADDR, -+ "to-addr '%s' is not a valid address" \ -+ % to_addr) -+ entry = (portStr(attrs["port"], "-"), attrs["protocol"], -+ portStr(to_port, "-"), str(to_addr)) -+ if entry not in self.item.forward_ports: -+ self.item.forward_ports.append(entry) -+ else: -+ log.warning("Forward port %s/%s%s%s already set, ignoring.", -+ attrs["port"], attrs["protocol"], -+ " >%s" % to_port if to_port else "", -+ " @%s" % to_addr if to_addr else "") -+ -+ elif name == "source-port": -+ if self._rule: -+ if self._rule.element: -+ log.warning("Invalid rule: More than one element in rule '%s', ignoring.", -+ str(self._rule)) -+ self._rule_error = True -+ return -+ self._rule.element = rich.Rich_SourcePort(attrs["port"], -+ attrs["protocol"]) -+ return -+ check_port(attrs["port"]) -+ check_tcpudp(attrs["protocol"]) -+ entry = (portStr(attrs["port"], "-"), attrs["protocol"]) -+ if entry not in self.item.source_ports: -+ self.item.source_ports.append(entry) -+ else: -+ log.warning("Source port '%s/%s' already set, ignoring.", -+ attrs["port"], attrs["protocol"]) -+ -+ elif name == "destination": -+ if not self._rule: -+ log.warning('Invalid rule: Destination outside of rule') -+ self._rule_error = True -+ return -+ if self._rule.destination: -+ log.warning("Invalid rule: More than one destination in rule '%s', ignoring.", -+ str(self._rule)) -+ return -+ invert = False -+ if "invert" in attrs and \ -+ attrs["invert"].lower() in [ "yes", "true" ]: -+ invert = True -+ self._rule.destination = rich.Rich_Destination(attrs["address"], -+ invert) -+ -+ elif name in [ "accept", "reject", "drop", "mark" ]: -+ if not self._rule: -+ log.warning('Invalid rule: Action outside of rule') -+ self._rule_error = True -+ return -+ if self._rule.action: -+ log.warning('Invalid rule: More than one action') -+ self._rule_error = True -+ return -+ if name == "accept": -+ self._rule.action = rich.Rich_Accept() -+ elif name == "reject": -+ _type = None -+ if "type" in attrs: -+ _type = attrs["type"] -+ self._rule.action = rich.Rich_Reject(_type) -+ elif name == "drop": -+ self._rule.action = rich.Rich_Drop() -+ elif name == "mark": -+ _set = attrs["set"] -+ self._rule.action = rich.Rich_Mark(_set) -+ self._limit_ok = self._rule.action -+ -+ elif name == "log": -+ if not self._rule: -+ log.warning('Invalid rule: Log outside of rule') -+ return -+ if self._rule.log: -+ log.warning('Invalid rule: More than one log') -+ return -+ level = None -+ if "level" in attrs: -+ level = attrs["level"] -+ if level not in [ "emerg", "alert", "crit", "error", -+ "warning", "notice", "info", "debug" ]: -+ log.warning('Invalid rule: Invalid log level') -+ self._rule_error = True -+ return -+ prefix = attrs["prefix"] if "prefix" in attrs else None -+ self._rule.log = rich.Rich_Log(prefix, level) -+ self._limit_ok = self._rule.log -+ -+ elif name == "audit": -+ if not self._rule: -+ log.warning('Invalid rule: Audit outside of rule') -+ return -+ if self._rule.audit: -+ log.warning("Invalid rule: More than one audit in rule '%s', ignoring.", -+ str(self._rule)) -+ self._rule_error = True -+ return -+ self._rule.audit = rich.Rich_Audit() -+ self._limit_ok = self._rule.audit -+ -+ elif name == "rule": -+ family = None -+ priority = 0 -+ if "family" in attrs: -+ family = attrs["family"] -+ if family not in [ "ipv4", "ipv6" ]: -+ log.warning('Invalid rule: Rule family "%s" invalid', -+ attrs["family"]) -+ self._rule_error = True -+ return -+ if "priority" in attrs: -+ priority = int(attrs["priority"]) -+ self._rule = rich.Rich_Rule(family=family, priority=priority) -+ -+ elif name == "limit": -+ if not self._limit_ok: -+ log.warning('Invalid rule: Limit outside of action, log and audit') -+ self._rule_error = True -+ return -+ if self._limit_ok.limit: -+ log.warning("Invalid rule: More than one limit in rule '%s', ignoring.", -+ str(self._rule)) -+ self._rule_error = True -+ return -+ value = attrs["value"] -+ self._limit_ok.limit = rich.Rich_Limit(value) -+ -+ else: -+ log.warning("Unknown XML element '%s'", name) -+ return -+ -+ def endElement(self, name): -+ IO_Object_ContentHandler.endElement(self, name) -+ -+ if name == "rule": -+ if not self._rule_error: -+ try: -+ self._rule.check() -+ except Exception as e: -+ log.warning("%s: %s", e, str(self._rule)) -+ else: -+ if str(self._rule) not in \ -+ [ str(x) for x in self.item.rules ]: -+ self.item.rules.append(self._rule) -+ else: -+ log.warning("Rule '%s' already set, ignoring.", -+ str(self._rule)) -+ self._rule = None -+ self._rule_error = False -+ elif name in [ "accept", "reject", "drop", "mark", "log", "audit" ]: -+ self._limit_ok = None -+ -+def policy_reader(filename, path, no_check_name=False): -+ policy = Policy() -+ if not filename.endswith(".xml"): -+ raise FirewallError(errors.INVALID_NAME, -+ "'%s' is missing .xml suffix" % filename) -+ policy.name = filename[:-4] -+ if not no_check_name: -+ policy.check_name(policy.name) -+ policy.filename = filename -+ policy.path = path -+ policy.builtin = False if path.startswith(config.ETC_FIREWALLD) else True -+ policy.default = policy.builtin -+ handler = policy_ContentHandler(policy) -+ parser = sax.make_parser() -+ parser.setContentHandler(handler) -+ name = "%s/%s" % (path, filename) -+ with open(name, "rb") as f: -+ source = sax.InputSource(None) -+ source.setByteStream(f) -+ try: -+ parser.parse(source) -+ except sax.SAXParseException as msg: -+ raise FirewallError(errors.INVALID_ZONE, -+ "not a valid policy file: %s" % \ -+ msg.getException()) -+ del handler -+ del parser -+ return policy -+ -+def policy_writer(policy, path=None): -+ _path = path if path else policy.path -+ -+ if policy.filename: -+ name = "%s/%s" % (_path, policy.filename) -+ else: -+ name = "%s/%s.xml" % (_path, policy.name) -+ -+ if os.path.exists(name): -+ try: -+ shutil.copy2(name, "%s.old" % name) -+ except Exception as msg: -+ log.error("Backup of file '%s' failed: %s", name, msg) -+ -+ dirpath = os.path.dirname(name) -+ if dirpath.startswith(config.ETC_FIREWALLD) and not os.path.exists(dirpath): -+ if not os.path.exists(config.ETC_FIREWALLD): -+ os.mkdir(config.ETC_FIREWALLD, 0o750) -+ os.mkdir(dirpath, 0o750) -+ -+ f = io.open(name, mode='wt', encoding='UTF-8') -+ handler = IO_Object_XMLGenerator(f) -+ handler.startDocument() -+ -+ # start policy element -+ attrs = {} -+ if policy.version and policy.version != "": -+ attrs["version"] = policy.version -+ if policy.priority != policy.priority_default: -+ attrs["priority"] = str(policy.priority) -+ attrs["target"] = policy.target -+ handler.startElement("policy", attrs) -+ handler.ignorableWhitespace("\n") -+ -+ # short -+ if policy.short and policy.short != "": -+ handler.ignorableWhitespace(" ") -+ handler.startElement("short", { }) -+ handler.characters(policy.short) -+ handler.endElement("short") -+ handler.ignorableWhitespace("\n") -+ -+ # description -+ if policy.description and policy.description != "": -+ handler.ignorableWhitespace(" ") -+ handler.startElement("description", { }) -+ handler.characters(policy.description) -+ handler.endElement("description") -+ handler.ignorableWhitespace("\n") -+ -+ # services -+ for service in uniqify(policy.services): -+ handler.ignorableWhitespace(" ") -+ handler.simpleElement("service", { "name": service }) -+ handler.ignorableWhitespace("\n") -+ -+ # ingress-zones -+ for zone in uniqify(policy.ingress_zones): -+ handler.ignorableWhitespace(" ") -+ handler.simpleElement("ingress-zone", { "name": zone }) -+ handler.ignorableWhitespace("\n") -+ -+ # egress-zones -+ for zone in uniqify(policy.ingress_zones): -+ handler.ignorableWhitespace(" ") -+ handler.simpleElement("egress-zone", { "name": zone }) -+ handler.ignorableWhitespace("\n") -+ -+ # ports -+ for port in uniqify(policy.ports): -+ handler.ignorableWhitespace(" ") -+ handler.simpleElement("port", { "port": port[0], "protocol": port[1] }) -+ handler.ignorableWhitespace("\n") -+ -+ # protocols -+ for protocol in uniqify(policy.protocols): -+ handler.ignorableWhitespace(" ") -+ handler.simpleElement("protocol", { "value": protocol }) -+ handler.ignorableWhitespace("\n") -+ -+ # icmp-blocks -+ for icmp in uniqify(policy.icmp_blocks): -+ handler.ignorableWhitespace(" ") -+ handler.simpleElement("icmp-block", { "name": icmp }) -+ handler.ignorableWhitespace("\n") -+ -+ # masquerade -+ if policy.masquerade: -+ handler.ignorableWhitespace(" ") -+ handler.simpleElement("masquerade", { }) -+ handler.ignorableWhitespace("\n") -+ -+ # forward-ports -+ for forward in uniqify(policy.forward_ports): -+ handler.ignorableWhitespace(" ") -+ attrs = { "port": forward[0], "protocol": forward[1] } -+ if forward[2] and forward[2] != "" : -+ attrs["to-port"] = forward[2] -+ if forward[3] and forward[3] != "" : -+ attrs["to-addr"] = forward[3] -+ handler.simpleElement("forward-port", attrs) -+ handler.ignorableWhitespace("\n") -+ -+ # source-ports -+ for port in uniqify(policy.source_ports): -+ handler.ignorableWhitespace(" ") -+ handler.simpleElement("source-port", { "port": port[0], -+ "protocol": port[1] }) -+ handler.ignorableWhitespace("\n") -+ -+ # rules -+ for rule in policy.rules: -+ attrs = { } -+ if rule.family: -+ attrs["family"] = rule.family -+ if rule.priority != 0: -+ attrs["priority"] = str(rule.priority) -+ handler.ignorableWhitespace(" ") -+ handler.startElement("rule", attrs) -+ handler.ignorableWhitespace("\n") -+ -+ # source -+ if rule.source: -+ attrs = { } -+ if rule.source.addr: -+ attrs["address"] = rule.source.addr -+ if rule.source.mac: -+ attrs["mac"] = rule.source.mac -+ if rule.source.ipset: -+ attrs["ipset"] = rule.source.ipset -+ if rule.source.invert: -+ attrs["invert"] = "True" -+ handler.ignorableWhitespace(" ") -+ handler.simpleElement("source", attrs) -+ handler.ignorableWhitespace("\n") -+ -+ # destination -+ if rule.destination: -+ attrs = { "address": rule.destination.addr } -+ if rule.destination.invert: -+ attrs["invert"] = "True" -+ handler.ignorableWhitespace(" ") -+ handler.simpleElement("destination", attrs) -+ handler.ignorableWhitespace("\n") -+ -+ # element -+ if rule.element: -+ element = "" -+ attrs = { } -+ -+ if type(rule.element) == rich.Rich_Service: -+ element = "service" -+ attrs["name"] = rule.element.name -+ elif type(rule.element) == rich.Rich_Port: -+ element = "port" -+ attrs["port"] = rule.element.port -+ attrs["protocol"] = rule.element.protocol -+ elif type(rule.element) == rich.Rich_Protocol: -+ element = "protocol" -+ attrs["value"] = rule.element.value -+ elif type(rule.element) == rich.Rich_Masquerade: -+ element = "masquerade" -+ elif type(rule.element) == rich.Rich_IcmpBlock: -+ element = "icmp-block" -+ attrs["name"] = rule.element.name -+ elif type(rule.element) == rich.Rich_IcmpType: -+ element = "icmp-type" -+ attrs["name"] = rule.element.name -+ elif type(rule.element) == rich.Rich_ForwardPort: -+ element = "forward-port" -+ attrs["port"] = rule.element.port -+ attrs["protocol"] = rule.element.protocol -+ if rule.element.to_port != "": -+ attrs["to-port"] = rule.element.to_port -+ if rule.element.to_address != "": -+ attrs["to-addr"] = rule.element.to_address -+ elif type(rule.element) == rich.Rich_SourcePort: -+ element = "source-port" -+ attrs["port"] = rule.element.port -+ attrs["protocol"] = rule.element.protocol -+ else: -+ raise FirewallError( -+ errors.INVALID_OBJECT, -+ "Unknown element '%s' in policy_writer" % type(rule.element)) -+ -+ handler.ignorableWhitespace(" ") -+ handler.simpleElement(element, attrs) -+ handler.ignorableWhitespace("\n") -+ -+ # rule.element -+ -+ # log -+ if rule.log: -+ attrs = { } -+ if rule.log.prefix: -+ attrs["prefix"] = rule.log.prefix -+ if rule.log.level: -+ attrs["level"] = rule.log.level -+ if rule.log.limit: -+ handler.ignorableWhitespace(" ") -+ handler.startElement("log", attrs) -+ handler.ignorableWhitespace("\n ") -+ handler.simpleElement("limit", -+ { "value": rule.log.limit.value }) -+ handler.ignorableWhitespace("\n ") -+ handler.endElement("log") -+ else: -+ handler.ignorableWhitespace(" ") -+ handler.simpleElement("log", attrs) -+ handler.ignorableWhitespace("\n") -+ -+ # audit -+ if rule.audit: -+ attrs = {} -+ if rule.audit.limit: -+ handler.ignorableWhitespace(" ") -+ handler.startElement("audit", { }) -+ handler.ignorableWhitespace("\n ") -+ handler.simpleElement("limit", -+ { "value": rule.audit.limit.value }) -+ handler.ignorableWhitespace("\n ") -+ handler.endElement("audit") -+ else: -+ handler.ignorableWhitespace(" ") -+ handler.simpleElement("audit", attrs) -+ handler.ignorableWhitespace("\n") -+ -+ # action -+ if rule.action: -+ action = "" -+ attrs = { } -+ if type(rule.action) == rich.Rich_Accept: -+ action = "accept" -+ elif type(rule.action) == rich.Rich_Reject: -+ action = "reject" -+ if rule.action.type: -+ attrs["type"] = rule.action.type -+ elif type(rule.action) == rich.Rich_Drop: -+ action = "drop" -+ elif type(rule.action) == rich.Rich_Mark: -+ action = "mark" -+ attrs["set"] = rule.action.set -+ else: -+ log.warning("Unknown action '%s'", type(rule.action)) -+ if rule.action.limit: -+ handler.ignorableWhitespace(" ") -+ handler.startElement(action, attrs) -+ handler.ignorableWhitespace("\n ") -+ handler.simpleElement("limit", -+ { "value": rule.action.limit.value }) -+ handler.ignorableWhitespace("\n ") -+ handler.endElement(action) -+ else: -+ handler.ignorableWhitespace(" ") -+ handler.simpleElement(action, attrs) -+ handler.ignorableWhitespace("\n") -+ -+ handler.ignorableWhitespace(" ") -+ handler.endElement("rule") -+ handler.ignorableWhitespace("\n") -+ -+ # end policy element -+ handler.endElement("policy") -+ handler.ignorableWhitespace("\n") -+ handler.endDocument() -+ f.close() -+ del handler -diff --git a/src/firewall/core/ipXtables.py b/src/firewall/core/ipXtables.py -index b1d6c20..3d05d7b 100644 ---- a/src/firewall/core/ipXtables.py -+++ b/src/firewall/core/ipXtables.py -@@ -22,7 +22,6 @@ - import os.path - import copy - --from firewall.core.base import SHORTCUTS, DEFAULT_ZONE_TARGET - from firewall.core.prog import runProg - from firewall.core.logger import log - from firewall.functions import tempFile, readfile, splitArgs, check_mac, portStr, \ -@@ -33,6 +32,8 @@ from firewall.core.rich import Rich_Accept, Rich_Reject, Rich_Drop, Rich_Mark, \ - Rich_Masquerade, Rich_ForwardPort, Rich_IcmpBlock - import string - -+POLICY_CHAIN_PREFIX = "pol_" -+ - BUILT_IN_CHAINS = { - "security": [ "INPUT", "OUTPUT", "FORWARD" ], - "raw": [ "PREROUTING", "OUTPUT" ], -@@ -167,7 +168,7 @@ def common_check_passthrough(args): - class ip4tables(object): - ipv = "ipv4" - name = "ip4tables" -- zones_supported = True -+ policies_supported = True - - def __init__(self, fw): - self._fw = fw -@@ -769,10 +770,9 @@ class ip4tables(object): - - return {} - -- def build_zone_source_interface_rules(self, enable, zone, interface, -+ def build_zone_source_interface_rules(self, enable, zone, policy, interface, - table, chain, append=False): -- # handle all zones in the same way here, now -- # trust and block zone targets are handled now in __chain -+ _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX) - opt = { - "PREROUTING": "-i", - "POSTROUTING": "-o", -@@ -782,7 +782,6 @@ class ip4tables(object): - "OUTPUT": "-o", - }[chain] - -- target = DEFAULT_ZONE_TARGET.format(chain=SHORTCUTS[chain], zone=zone) - action = "-g" - - if enable and not append: -@@ -793,13 +792,14 @@ class ip4tables(object): - rule = [ "-D", "%s_ZONES" % chain ] - if not append: - rule += ["%%ZONE_INTERFACE%%"] -- rule += [ "-t", table, opt, interface, action, target ] -+ rule += [ "-t", table, opt, interface, action, _policy ] - return [rule] - -- def build_zone_source_address_rules(self, enable, zone, -+ def build_zone_source_address_rules(self, enable, zone, policy, - address, table, chain): - add_del = { True: "-I", False: "-D" }[enable] - -+ _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX) - opt = { - "PREROUTING": "-s", - "POSTROUTING": "-d", -@@ -814,7 +814,6 @@ class ip4tables(object): - else: - zone_dispatch_chain = "%s_ZONES" % (chain) - -- target = DEFAULT_ZONE_TARGET.format(chain=SHORTCUTS[chain], zone=zone) - action = "-g" - - if address.startswith("ipset:"): -@@ -828,7 +827,7 @@ class ip4tables(object): - "%%ZONE_SOURCE%%", zone, - "-t", table, - "-m", "set", "--match-set", name, -- flags, action, target ] -+ flags, action, _policy ] - else: - if check_mac(address): - # outgoing can not be set -@@ -838,7 +837,7 @@ class ip4tables(object): - "%%ZONE_SOURCE%%", zone, - "-t", table, - "-m", "mac", "--mac-source", address.upper(), -- action, target ] -+ action, _policy ] - else: - if check_single_address("ipv6", address): - address = normalizeIP6(address) -@@ -848,56 +847,48 @@ class ip4tables(object): - rule = [ add_del, zone_dispatch_chain, - "%%ZONE_SOURCE%%", zone, - "-t", table, -- opt, address, action, target ] -+ opt, address, action, _policy ] - return [rule] - -- def build_zone_chain_rules(self, zone, table, chain): -- _zone = DEFAULT_ZONE_TARGET.format(chain=SHORTCUTS[chain], zone=zone) -+ def build_policy_chain_rules(self, policy, table): -+ _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX) - -- self.our_chains[table].update(set([_zone, -- "%s_log" % _zone, -- "%s_deny" % _zone, -- "%s_pre" % _zone, -- "%s_post" % _zone, -- "%s_allow" % _zone])) -+ self.our_chains[table].update(set([_policy, -+ "%s_log" % _policy, -+ "%s_deny" % _policy, -+ "%s_pre" % _policy, -+ "%s_post" % _policy, -+ "%s_allow" % _policy])) - - rules = [] -- rules.append([ "-N", _zone, "-t", table ]) -- rules.append([ "-N", "%s_pre" % _zone, "-t", table ]) -- rules.append([ "-N", "%s_log" % _zone, "-t", table ]) -- rules.append([ "-N", "%s_deny" % _zone, "-t", table ]) -- rules.append([ "-N", "%s_allow" % _zone, "-t", table ]) -- rules.append([ "-N", "%s_post" % _zone, "-t", table ]) -- rules.append([ "-A", _zone, "-t", table, "-j", "%s_pre" % _zone ]) -- rules.append([ "-A", _zone, "-t", table, "-j", "%s_log" % _zone ]) -- rules.append([ "-A", _zone, "-t", table, "-j", "%s_deny" % _zone ]) -- rules.append([ "-A", _zone, "-t", table, "-j", "%s_allow" % _zone ]) -- rules.append([ "-A", _zone, "-t", table, "-j", "%s_post" % _zone ]) -- -- target = self._fw.zone._zones[zone].target -+ rules.append([ "-N", _policy, "-t", table ]) -+ rules.append([ "-N", "%s_pre" % _policy, "-t", table ]) -+ rules.append([ "-N", "%s_log" % _policy, "-t", table ]) -+ rules.append([ "-N", "%s_deny" % _policy, "-t", table ]) -+ rules.append([ "-N", "%s_allow" % _policy, "-t", table ]) -+ rules.append([ "-N", "%s_post" % _policy, "-t", table ]) -+ rules.append([ "-A", _policy, "-t", table, "-j", "%s_pre" % _policy ]) -+ rules.append([ "-A", _policy, "-t", table, "-j", "%s_log" % _policy ]) -+ rules.append([ "-A", _policy, "-t", table, "-j", "%s_deny" % _policy ]) -+ rules.append([ "-A", _policy, "-t", table, "-j", "%s_allow" % _policy ]) -+ rules.append([ "-A", _policy, "-t", table, "-j", "%s_post" % _policy ]) -+ -+ target = self._fw.policy._policies[policy].target - - if self._fw.get_log_denied() != "off": -- if table == "filter" and \ -- chain in [ "INPUT", "FORWARD_IN", "FORWARD_OUT", "OUTPUT" ]: -+ if table == "filter": - if target in [ "REJECT", "%%REJECT%%" ]: -- rules.append([ "-A", _zone, "-t", table, "%%LOGTYPE%%", -+ rules.append([ "-A", _policy, "-t", table, "%%LOGTYPE%%", - "-j", "LOG", "--log-prefix", -- "\"%s_REJECT: \"" % _zone ]) -+ "\"%s_REJECT: \"" % _policy ]) - if target == "DROP": -- rules.append([ "-A", _zone, "-t", table, "%%LOGTYPE%%", -+ rules.append([ "-A", _policy, "-t", table, "%%LOGTYPE%%", - "-j", "LOG", "--log-prefix", -- "\"%s_DROP: \"" % _zone ]) -- -- # Handle trust, block and drop zones: -- # Add an additional rule with the zone target (accept, reject -- # or drop) to the base zone only in the filter table. -- # Otherwise it is not be possible to have a zone with drop -- # target, that is allowing traffic that is locally initiated -- # or that adds additional rules. (RHBZ#1055190) -+ "\"%s_DROP: \"" % _policy ]) -+ - if table == "filter" and \ -- target in [ "ACCEPT", "REJECT", "%%REJECT%%", "DROP" ] and \ -- chain in [ "INPUT", "FORWARD_IN", "FORWARD_OUT", "OUTPUT" ]: -- rules.append([ "-A", _zone, "-t", table, "-j", target ]) -+ target in [ "ACCEPT", "REJECT", "%%REJECT%%", "DROP" ]: -+ rules.append([ "-A", _policy, "-t", table, "-j", target ]) - - return rules - -@@ -944,14 +935,16 @@ class ip4tables(object): - return [] - return ["%%RICH_RULE_PRIORITY%%", rich_rule.priority] - -- def _rich_rule_log(self, rich_rule, enable, table, target, rule_fragment): -+ def _rich_rule_log(self, policy, rich_rule, enable, table, rule_fragment): - if not rich_rule.log: - return [] - -+ _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX) -+ - add_del = { True: "-A", False: "-D" }[enable] - - chain_suffix = self._rich_rule_chain_suffix_from_log(rich_rule) -- rule = ["-t", table, add_del, "%s_%s" % (target, chain_suffix)] -+ rule = ["-t", table, add_del, "%s_%s" % (_policy, chain_suffix)] - rule += self._rich_rule_priority_fragment(rich_rule) - rule += rule_fragment + [ "-j", "LOG" ] - if rich_rule.log.prefix: -@@ -962,14 +955,16 @@ class ip4tables(object): - - return rule - -- def _rich_rule_audit(self, rich_rule, enable, table, target, rule_fragment): -+ def _rich_rule_audit(self, policy, rich_rule, enable, table, rule_fragment): - if not rich_rule.audit: - return [] - - add_del = { True: "-A", False: "-D" }[enable] - -+ _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX) -+ - chain_suffix = self._rich_rule_chain_suffix_from_log(rich_rule) -- rule = ["-t", table, add_del, "%s_%s" % (target, chain_suffix)] -+ rule = ["-t", table, add_del, "%s_%s" % (_policy, chain_suffix)] - rule += self._rich_rule_priority_fragment(rich_rule) - rule += rule_fragment - if type(rich_rule.action) == Rich_Accept: -@@ -985,14 +980,16 @@ class ip4tables(object): - - return rule - -- def _rich_rule_action(self, zone, rich_rule, enable, table, target, rule_fragment): -+ def _rich_rule_action(self, policy, rich_rule, enable, table, rule_fragment): - if not rich_rule.action: - return [] - - add_del = { True: "-A", False: "-D" }[enable] - -+ _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX) -+ - chain_suffix = self._rich_rule_chain_suffix(rich_rule) -- chain = "%s_%s" % (target, chain_suffix) -+ chain = "%s_%s" % (_policy, chain_suffix) - if type(rich_rule.action) == Rich_Accept: - rule_action = [ "-j", "ACCEPT" ] - elif type(rich_rule.action) == Rich_Reject: -@@ -1002,10 +999,9 @@ class ip4tables(object): - elif type(rich_rule.action) == Rich_Drop: - rule_action = [ "-j", "DROP" ] - elif type(rich_rule.action) == Rich_Mark: -- target = DEFAULT_ZONE_TARGET.format(chain=SHORTCUTS["PREROUTING"], -- zone=zone) - table = "mangle" -- chain = "%s_%s" % (target, chain_suffix) -+ _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX) -+ chain = "%s_%s" % (_policy, chain_suffix) - rule_action = [ "-j", "MARK", "--set-xmark", rich_rule.action.set ] - else: - raise FirewallError(INVALID_RULE, -@@ -1064,11 +1060,10 @@ class ip4tables(object): - - return rule_fragment - -- def build_zone_ports_rules(self, enable, zone, proto, port, destination=None, rich_rule=None): -+ def build_policy_ports_rules(self, enable, policy, proto, port, destination=None, rich_rule=None): - add_del = { True: "-A", False: "-D" }[enable] - table = "filter" -- target = DEFAULT_ZONE_TARGET.format(chain=SHORTCUTS["INPUT"], -- zone=zone) -+ _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX) - - rule_fragment = [ "-p", proto ] - if port: -@@ -1083,19 +1078,19 @@ class ip4tables(object): - - rules = [] - if rich_rule: -- rules.append(self._rich_rule_log(rich_rule, enable, table, target, rule_fragment)) -- rules.append(self._rich_rule_audit(rich_rule, enable, table, target, rule_fragment)) -- rules.append(self._rich_rule_action(zone, rich_rule, enable, table, target, rule_fragment)) -+ rules.append(self._rich_rule_log(policy, rich_rule, enable, table, rule_fragment)) -+ rules.append(self._rich_rule_audit(policy, rich_rule, enable, table, rule_fragment)) -+ rules.append(self._rich_rule_action(policy, rich_rule, enable, table, rule_fragment)) - else: -- rules.append([add_del, "%s_allow" % (target), "-t", table] + -+ rules.append([add_del, "%s_allow" % (_policy), "-t", table] + - rule_fragment + [ "-j", "ACCEPT" ]) - - return rules - -- def build_zone_protocol_rules(self, enable, zone, protocol, destination=None, rich_rule=None): -+ def build_policy_protocol_rules(self, enable, policy, protocol, destination=None, rich_rule=None): - add_del = { True: "-A", False: "-D" }[enable] - table = "filter" -- target = DEFAULT_ZONE_TARGET.format(chain=SHORTCUTS["INPUT"], zone=zone) -+ _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX) - - rule_fragment = [ "-p", protocol ] - if destination: -@@ -1108,20 +1103,20 @@ class ip4tables(object): - - rules = [] - if rich_rule: -- rules.append(self._rich_rule_log(rich_rule, enable, table, target, rule_fragment)) -- rules.append(self._rich_rule_audit(rich_rule, enable, table, target, rule_fragment)) -- rules.append(self._rich_rule_action(zone, rich_rule, enable, table, target, rule_fragment)) -+ rules.append(self._rich_rule_log(policy, rich_rule, enable, table, rule_fragment)) -+ rules.append(self._rich_rule_audit(policy, rich_rule, enable, table, rule_fragment)) -+ rules.append(self._rich_rule_action(policy, rich_rule, enable, table, rule_fragment)) - else: -- rules.append([add_del, "%s_allow" % (target), "-t", table] + -+ rules.append([add_del, "%s_allow" % (_policy), "-t", table] + - rule_fragment + [ "-j", "ACCEPT" ]) - - return rules - -- def build_zone_source_ports_rules(self, enable, zone, proto, port, -+ def build_policy_source_ports_rules(self, enable, policy, proto, port, - destination=None, rich_rule=None): - add_del = { True: "-A", False: "-D" }[enable] - table = "filter" -- target = DEFAULT_ZONE_TARGET.format(chain=SHORTCUTS["INPUT"], zone=zone) -+ _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX) - - rule_fragment = [ "-p", proto ] - if port: -@@ -1136,21 +1131,22 @@ class ip4tables(object): - - rules = [] - if rich_rule: -- rules.append(self._rich_rule_log(rich_rule, enable, table, target, rule_fragment)) -- rules.append(self._rich_rule_audit(rich_rule, enable, table, target, rule_fragment)) -- rules.append(self._rich_rule_action(zone, rich_rule, enable, table, target, rule_fragment)) -+ rules.append(self._rich_rule_log(policy, rich_rule, enable, table, rule_fragment)) -+ rules.append(self._rich_rule_audit(policy, rich_rule, enable, table, rule_fragment)) -+ rules.append(self._rich_rule_action(policy, rich_rule, enable, table, rule_fragment)) - else: -- rules.append([add_del, "%s_allow" % (target), "-t", table] + -+ rules.append([add_del, "%s_allow" % (_policy), "-t", table] + - rule_fragment + [ "-j", "ACCEPT" ]) - - return rules - -- def build_zone_helper_ports_rules(self, enable, zone, proto, port, -+ def build_policy_helper_ports_rules(self, enable, policy, proto, port, - destination, helper_name, module_short_name): -+ table = "raw" -+ _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX) - add_del = { True: "-A", False: "-D" }[enable] -- target = DEFAULT_ZONE_TARGET.format(chain=SHORTCUTS["PREROUTING"], -- zone=zone) -- rule = [ add_del, "%s_allow" % (target), "-t", "raw", "-p", proto ] -+ -+ rule = [ add_del, "%s_allow" % (_policy), "-t", "raw", "-p", proto ] - if port: - rule += [ "--dport", "%s" % portStr(port) ] - if destination: -@@ -1159,10 +1155,11 @@ class ip4tables(object): - - return [rule] - -- def build_zone_masquerade_rules(self, enable, zone, rich_rule=None): -+ def build_policy_masquerade_rules(self, enable, policy, rich_rule=None): -+ table = "nat" -+ _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX) - add_del = { True: "-A", False: "-D" }[enable] -- target = DEFAULT_ZONE_TARGET.format(chain=SHORTCUTS["POSTROUTING"], -- zone=zone) -+ - rule_fragment = [] - if rich_rule: - chain_suffix = self._rich_rule_chain_suffix(rich_rule) -@@ -1173,12 +1170,10 @@ class ip4tables(object): - chain_suffix = "allow" - - rules = [] -- rules.append(["-t", "nat", add_del, "%s_%s" % (target, chain_suffix)] -+ rules.append(["-t", "nat", add_del, "%s_%s" % (_policy, chain_suffix)] - + rule_fragment + - [ "!", "-o", "lo", "-j", "MASQUERADE" ]) -- # FORWARD_OUT -- target = DEFAULT_ZONE_TARGET.format(chain=SHORTCUTS["FORWARD_OUT"], -- zone=zone) -+ - rule_fragment = [] - if rich_rule: - chain_suffix = self._rich_rule_chain_suffix(rich_rule) -@@ -1188,14 +1183,18 @@ class ip4tables(object): - else: - chain_suffix = "allow" - -- rules.append(["-t", "filter", add_del, "%s_%s" % (target, chain_suffix)] -+ table = "filter" -+ _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX) -+ rules.append(["-t", "filter", add_del, "%s_%s" % (_policy, chain_suffix)] - + rule_fragment + - ["-m", "conntrack", "--ctstate", "NEW,UNTRACKED", "-j", "ACCEPT" ]) - - return rules - -- def build_zone_forward_port_rules(self, enable, zone, port, -+ def build_policy_forward_port_rules(self, enable, policy, port, - protocol, toport, toaddr, rich_rule=None): -+ table = "nat" -+ _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX) - add_del = { True: "-A", False: "-D" }[enable] - - to = "" -@@ -1207,9 +1206,6 @@ class ip4tables(object): - if toport and toport != "": - to += ":%s" % portStr(toport, "-") - -- target = DEFAULT_ZONE_TARGET.format(chain=SHORTCUTS["PREROUTING"], -- zone=zone) -- - rule_fragment = [] - if rich_rule: - chain_suffix = self._rich_rule_chain_suffix(rich_rule) -@@ -1221,16 +1217,17 @@ class ip4tables(object): - - rules = [] - if rich_rule: -- rules.append(self._rich_rule_log(rich_rule, enable, "nat", target, rule_fragment)) -- rules.append(["-t", "nat", add_del, "%s_%s" % (target, chain_suffix)] -+ rules.append(self._rich_rule_log(policy, rich_rule, enable, "nat", rule_fragment)) -+ rules.append(["-t", "nat", add_del, "%s_%s" % (_policy, chain_suffix)] - + rule_fragment + - ["-p", protocol, "--dport", portStr(port), - "-j", "DNAT", "--to-destination", to]) - - return rules - -- def build_zone_icmp_block_rules(self, enable, zone, ict, rich_rule=None): -+ def build_policy_icmp_block_rules(self, enable, policy, ict, rich_rule=None): - table = "filter" -+ _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX) - add_del = { True: "-A", False: "-D" }[enable] - - if self.ipv == "ipv4": -@@ -1241,93 +1238,87 @@ class ip4tables(object): - match = [ "-m", "icmp6", "--icmpv6-type", ict.name ] - - rules = [] -- for chain in ["INPUT", "FORWARD_IN"]: -- target = DEFAULT_ZONE_TARGET.format(chain=SHORTCUTS[chain], -- zone=zone) -- if self._fw.zone.query_icmp_block_inversion(zone): -- final_chain = "%s_allow" % target -- final_target = "ACCEPT" -- else: -- final_chain = "%s_deny" % target -- final_target = "%%REJECT%%" -- -- rule_fragment = [] -- if rich_rule: -- rule_fragment += self._rich_rule_destination_fragment(rich_rule.destination) -- rule_fragment += self._rich_rule_source_fragment(rich_rule.source) -- rule_fragment += proto + match -- -- if rich_rule: -- rules.append(self._rich_rule_log(rich_rule, enable, table, target, rule_fragment)) -- rules.append(self._rich_rule_audit(rich_rule, enable, table, target, rule_fragment)) -- if rich_rule.action: -- rules.append(self._rich_rule_action(zone, rich_rule, enable, table, target, rule_fragment)) -- else: -- chain_suffix = self._rich_rule_chain_suffix(rich_rule) -- rules.append(["-t", table, add_del, "%s_%s" % (target, chain_suffix)] -- + self._rich_rule_priority_fragment(rich_rule) -- + rule_fragment + -- [ "-j", "%%REJECT%%" ]) -+ if self._fw.policy.query_icmp_block_inversion(policy): -+ final_chain = "%s_allow" % (_policy) -+ final_target = "ACCEPT" -+ else: -+ final_chain = "%s_deny" % (_policy) -+ final_target = "%%REJECT%%" -+ -+ rule_fragment = [] -+ if rich_rule: -+ rule_fragment += self._rich_rule_destination_fragment(rich_rule.destination) -+ rule_fragment += self._rich_rule_source_fragment(rich_rule.source) -+ rule_fragment += proto + match -+ -+ if rich_rule: -+ rules.append(self._rich_rule_log(policy, rich_rule, enable, table, rule_fragment)) -+ rules.append(self._rich_rule_audit(policy, rich_rule, enable, table, rule_fragment)) -+ if rich_rule.action: -+ rules.append(self._rich_rule_action(policy, rich_rule, enable, table, rule_fragment)) - else: -- if self._fw.get_log_denied() != "off" and final_target != "ACCEPT": -- rules.append([ add_del, final_chain, "-t", table ] -- + rule_fragment + -- [ "%%LOGTYPE%%", "-j", "LOG", -- "--log-prefix", "\"%s_ICMP_BLOCK: \"" % zone ]) -+ chain_suffix = self._rich_rule_chain_suffix(rich_rule) -+ rules.append(["-t", table, add_del, "%s_%s" % (_policy, chain_suffix)] -+ + self._rich_rule_priority_fragment(rich_rule) -+ + rule_fragment + -+ [ "-j", "%%REJECT%%" ]) -+ else: -+ if self._fw.get_log_denied() != "off" and final_target != "ACCEPT": - rules.append([ add_del, final_chain, "-t", table ] - + rule_fragment + -- [ "-j", final_target ]) -+ [ "%%LOGTYPE%%", "-j", "LOG", -+ "--log-prefix", "\"%s_ICMP_BLOCK: \"" % policy ]) -+ rules.append([ add_del, final_chain, "-t", table ] -+ + rule_fragment + -+ [ "-j", final_target ]) - - return rules - -- def build_zone_icmp_block_inversion_rules(self, enable, zone): -+ def build_policy_icmp_block_inversion_rules(self, enable, policy): - table = "filter" -+ _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX) -+ - rules = [] -- for chain in [ "INPUT", "FORWARD_IN" ]: -- rule_idx = 6 -- _zone = DEFAULT_ZONE_TARGET.format(chain=SHORTCUTS[chain], -- zone=zone) -+ rule_idx = 6 - -- if self._fw.zone.query_icmp_block_inversion(zone): -- ibi_target = "%%REJECT%%" -+ if self._fw.policy.query_icmp_block_inversion(policy): -+ ibi_target = "%%REJECT%%" - -- if self._fw.get_log_denied() != "off": -- if enable: -- rule = [ "-I", _zone, str(rule_idx) ] -- else: -- rule = [ "-D", _zone ] -- -- rule = rule + [ "-t", table, "-p", "%%ICMP%%", -- "%%LOGTYPE%%", -- "-j", "LOG", "--log-prefix", -- "\"%s_ICMP_BLOCK: \"" % _zone ] -- rules.append(rule) -- rule_idx += 1 -- else: -- ibi_target = "ACCEPT" -+ if self._fw.get_log_denied() != "off": -+ if enable: -+ rule = [ "-I", _policy, str(rule_idx) ] -+ else: -+ rule = [ "-D", _policy ] -+ -+ rule = rule + [ "-t", table, "-p", "%%ICMP%%", -+ "%%LOGTYPE%%", -+ "-j", "LOG", "--log-prefix", -+ "\"%s_ICMP_BLOCK: \"" % _policy ] -+ rules.append(rule) -+ rule_idx += 1 -+ else: -+ ibi_target = "ACCEPT" - -- if enable: -- rule = [ "-I", _zone, str(rule_idx) ] -- else: -- rule = [ "-D", _zone ] -- rule = rule + [ "-t", table, "-p", "%%ICMP%%", "-j", ibi_target ] -- rules.append(rule) -+ if enable: -+ rule = [ "-I", _policy, str(rule_idx) ] -+ else: -+ rule = [ "-D", _policy ] -+ rule = rule + [ "-t", table, "-p", "%%ICMP%%", "-j", ibi_target ] -+ rules.append(rule) - - return rules - -- def build_zone_rich_source_destination_rules(self, enable, zone, rich_rule): -+ def build_policy_rich_source_destination_rules(self, enable, policy, rich_rule): - table = "filter" -- target = DEFAULT_ZONE_TARGET.format(chain=SHORTCUTS["INPUT"], -- zone=zone) - - rule_fragment = [] - rule_fragment += self._rich_rule_destination_fragment(rich_rule.destination) - rule_fragment += self._rich_rule_source_fragment(rich_rule.source) - - rules = [] -- rules.append(self._rich_rule_log(rich_rule, enable, table, target, rule_fragment)) -- rules.append(self._rich_rule_audit(rich_rule, enable, table, target, rule_fragment)) -- rules.append(self._rich_rule_action(zone, rich_rule, enable, table, target, rule_fragment)) -+ rules.append(self._rich_rule_log(policy, rich_rule, enable, table, rule_fragment)) -+ rules.append(self._rich_rule_audit(policy, rich_rule, enable, table, rule_fragment)) -+ rules.append(self._rich_rule_action(policy, rich_rule, enable, table, rule_fragment)) - - return rules - -diff --git a/src/firewall/core/nftables.py b/src/firewall/core/nftables.py -index a6ad5f7..f422556 100644 ---- a/src/firewall/core/nftables.py -+++ b/src/firewall/core/nftables.py -@@ -23,7 +23,6 @@ from __future__ import absolute_import - import copy - import json - --from firewall.core.base import SHORTCUTS, DEFAULT_ZONE_TARGET - from firewall.core.logger import log - from firewall.functions import check_mac, getPortRange, normalizeIP6, \ - check_single_address, check_address -@@ -36,6 +35,7 @@ from nftables.nftables import Nftables - - TABLE_NAME = "firewalld" - TABLE_NAME_POLICY = TABLE_NAME + "_" + "policy_drop" -+POLICY_CHAIN_PREFIX = "policy_" - - # Map iptables (table, chain) to hooks and priorities. - # These are well defined by NF_IP_PRI_* defines in netfilter. -@@ -158,7 +158,7 @@ ICMP_TYPES_FRAGMENTS = { - - class nftables(object): - name = "nftables" -- zones_supported = True -+ policies_supported = True - - def __init__(self, fw): - self._fw = fw -@@ -174,7 +174,6 @@ class nftables(object): - self.nftables.set_echo_output(True) - self.nftables.set_handle_output(True) - -- - def _run_replace_zone_source(self, rule, zone_source_index_cache): - for verb in ["add", "insert", "delete"]: - if verb in rule: -@@ -698,18 +697,19 @@ class nftables(object): - - return [] - -- def build_zone_source_interface_rules(self, enable, zone, interface, -+ def build_zone_source_interface_rules(self, enable, zone, policy, interface, - table, chain, append=False, - family="inet"): - # nat tables needs to use ip/ip6 family - if table == "nat" and family == "inet": - rules = [] -- rules.extend(self.build_zone_source_interface_rules(enable, zone, -+ rules.extend(self.build_zone_source_interface_rules(enable, zone, policy, - interface, table, chain, append, "ip")) -- rules.extend(self.build_zone_source_interface_rules(enable, zone, -+ rules.extend(self.build_zone_source_interface_rules(enable, zone, policy, - interface, table, chain, append, "ip6")) - return rules - -+ _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX) - opt = { - "PREROUTING": "iifname", - "POSTROUTING": "oifname", -@@ -722,16 +722,15 @@ class nftables(object): - if interface[len(interface)-1] == "+": - interface = interface[:len(interface)-1] + "*" - -- target = DEFAULT_ZONE_TARGET.format(chain=SHORTCUTS[chain], zone=zone) - action = "goto" - - if interface == "*": -- expr_fragments = [{action: {"target": "%s_%s" % (table, target)}}] -+ expr_fragments = [{action: {"target": "%s_%s" % (table, _policy)}}] - else: - expr_fragments = [{"match": {"left": {"meta": {"key": opt}}, - "op": "==", - "right": interface}}, -- {action: {"target": "%s_%s" % (table, target)}}] -+ {action: {"target": "%s_%s" % (table, _policy)}}] - - if enable and not append: - verb = "insert" -@@ -757,7 +756,7 @@ class nftables(object): - - return [{verb: {"rule": rule}}] - -- def build_zone_source_address_rules(self, enable, zone, -+ def build_zone_source_address_rules(self, enable, zone, policy, - address, table, chain, family="inet"): - # nat tables needs to use ip/ip6 family - if table == "nat" and family == "inet": -@@ -768,13 +767,14 @@ class nftables(object): - ipset_family = None - - if check_address("ipv4", address) or check_mac(address) or ipset_family == "ip": -- rules.extend(self.build_zone_source_address_rules(enable, zone, -+ rules.extend(self.build_zone_source_address_rules(enable, zone, policy, - address, table, chain, "ip")) - if check_address("ipv6", address) or check_mac(address) or ipset_family == "ip6": -- rules.extend(self.build_zone_source_address_rules(enable, zone, -+ rules.extend(self.build_zone_source_address_rules(enable, zone, policy, - address, table, chain, "ip6")) - return rules - -+ _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX) - add_del = { True: "insert", False: "delete" }[enable] - - opt = { -@@ -791,73 +791,64 @@ class nftables(object): - else: - zone_dispatch_chain = "%s_%s_ZONES" % (table, chain) - -- target = DEFAULT_ZONE_TARGET.format(chain=SHORTCUTS[chain], zone=zone) - action = "goto" - - rule = {"family": family, - "table": TABLE_NAME, - "chain": zone_dispatch_chain, - "expr": [self._rule_addr_fragment(opt, address), -- {action: {"target": "%s_%s" % (table, target)}}]} -+ {action: {"target": "%s_%s" % (table, _policy)}}]} - rule.update(self._zone_source_fragment(zone, address)) - return [{add_del: {"rule": rule}}] - -- def build_zone_chain_rules(self, zone, table, chain, family="inet"): -+ def build_policy_chain_rules(self, policy, table, family="inet"): - # nat tables needs to use ip/ip6 family - if table == "nat" and family == "inet": - rules = [] -- rules.extend(self.build_zone_chain_rules(zone, table, chain, "ip")) -- rules.extend(self.build_zone_chain_rules(zone, table, chain, "ip6")) -+ rules.extend(self.build_policy_chain_rules(policy, table, "ip")) -+ rules.extend(self.build_policy_chain_rules(policy, table, "ip6")) - return rules - -- _zone = DEFAULT_ZONE_TARGET.format(chain=SHORTCUTS[chain], zone=zone) -+ _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX) - - rules = [] - rules.append({"add": {"chain": {"family": family, - "table": TABLE_NAME, -- "name": "%s_%s" % (table, _zone)}}}) -+ "name": "%s_%s" % (table, _policy)}}}) - for chain_suffix in ["pre", "log", "deny", "allow", "post"]: - rules.append({"add": {"chain": {"family": family, - "table": TABLE_NAME, -- "name": "%s_%s_%s" % (table, _zone, chain_suffix)}}}) -+ "name": "%s_%s_%s" % (table, _policy, chain_suffix)}}}) - - for chain_suffix in ["pre", "log", "deny", "allow", "post"]: - rules.append({"add": {"rule": {"family": family, - "table": TABLE_NAME, -- "chain": "%s_%s" % (table, _zone), -- "expr": [{"jump": {"target": "%s_%s_%s" % (table, _zone, chain_suffix)}}]}}}) -+ "chain": "%s_%s" % (table, _policy), -+ "expr": [{"jump": {"target": "%s_%s_%s" % (table, _policy, chain_suffix)}}]}}}) - -- target = self._fw.zone._zones[zone].target -+ target = self._fw.policy._policies[policy].target - - if self._fw.get_log_denied() != "off": -- if table == "filter" and \ -- chain in ["INPUT", "FORWARD_IN", "FORWARD_OUT", "OUTPUT"]: -+ if table == "filter": - if target in ["REJECT", "%%REJECT%%", "DROP"]: - log_suffix = target - if target == "%%REJECT%%": - log_suffix = "REJECT" - rules.append({"add": {"rule": {"family": family, - "table": TABLE_NAME, -- "chain": "%s_%s" % (table, _zone), -+ "chain": "%s_%s" % (table, _policy), - "expr": [self._pkttype_match_fragment(self._fw.get_log_denied()), -- {"log": {"prefix": "\"filter_%s_%s: \"" % (_zone, log_suffix)}}]}}}) -- -- # Handle trust, block and drop zones: -- # Add an additional rule with the zone target (accept, reject -- # or drop) to the base zone only in the filter table. -- # Otherwise it is not be possible to have a zone with drop -- # target, that is allowing traffic that is locally initiated -- # or that adds additional rules. (RHBZ#1055190) -+ {"log": {"prefix": "\"filter_%s_%s: \"" % (_policy, log_suffix)}}]}}}) -+ - if table == "filter" and \ -- target in ["ACCEPT", "REJECT", "%%REJECT%%", "DROP"] and \ -- chain in ["INPUT", "FORWARD_IN", "FORWARD_OUT", "OUTPUT"]: -+ target in ["ACCEPT", "REJECT", "%%REJECT%%", "DROP"]: - if target == "%%REJECT%%": - target_fragment = self._reject_fragment() - else: - target_fragment = {target.lower(): None} - rules.append({"add": {"rule": {"family": family, - "table": TABLE_NAME, -- "chain": "%s_%s" % (table, _zone), -+ "chain": "%s_%s" % (table, _policy), - "expr": [target_fragment]}}}) - - return rules -@@ -981,10 +972,12 @@ class nftables(object): - return {} - return {"%%RICH_RULE_PRIORITY%%": rich_rule.priority} - -- def _rich_rule_log(self, rich_rule, enable, table, target, expr_fragments): -+ def _rich_rule_log(self, policy, rich_rule, enable, table, expr_fragments): - if not rich_rule.log: - return {} - -+ _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX) -+ - add_del = { True: "add", False: "delete" }[enable] - - chain_suffix = self._rich_rule_chain_suffix_from_log(rich_rule) -@@ -997,37 +990,41 @@ class nftables(object): - - rule = {"family": "inet", - "table": TABLE_NAME, -- "chain": "%s_%s_%s" % (table, target, chain_suffix), -+ "chain": "%s_%s_%s" % (table, _policy, chain_suffix), - "expr": expr_fragments + - [{"log": log_options}, - self._rich_rule_limit_fragment(rich_rule.log.limit)]} - rule.update(self._rich_rule_priority_fragment(rich_rule)) - return {add_del: {"rule": rule}} - -- def _rich_rule_audit(self, rich_rule, enable, table, target, expr_fragments): -+ def _rich_rule_audit(self, policy, rich_rule, enable, table, expr_fragments): - if not rich_rule.audit: - return {} - -+ _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX) -+ - add_del = { True: "add", False: "delete" }[enable] - - chain_suffix = self._rich_rule_chain_suffix_from_log(rich_rule) - rule = {"family": "inet", - "table": TABLE_NAME, -- "chain": "%s_%s_%s" % (table, target, chain_suffix), -+ "chain": "%s_%s_%s" % (table, _policy, chain_suffix), - "expr": expr_fragments + - [{"log": {"level": "audit"}}, - self._rich_rule_limit_fragment(rich_rule.audit.limit)]} - rule.update(self._rich_rule_priority_fragment(rich_rule)) - return {add_del: {"rule": rule}} - -- def _rich_rule_action(self, zone, rich_rule, enable, table, target, expr_fragments): -+ def _rich_rule_action(self, policy, rich_rule, enable, table, expr_fragments): - if not rich_rule.action: - return {} - -+ _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX) -+ - add_del = { True: "add", False: "delete" }[enable] - - chain_suffix = self._rich_rule_chain_suffix(rich_rule) -- chain = "%s_%s_%s" % (table, target, chain_suffix) -+ chain = "%s_%s_%s" % (table, _policy, chain_suffix) - if type(rich_rule.action) == Rich_Accept: - rule_action = {"accept": None} - elif type(rich_rule.action) == Rich_Reject: -@@ -1038,10 +1035,9 @@ class nftables(object): - elif type(rich_rule.action) == Rich_Drop: - rule_action = {"drop": None} - elif type(rich_rule.action) == Rich_Mark: -- target = DEFAULT_ZONE_TARGET.format(chain=SHORTCUTS["PREROUTING"], -- zone=zone) - table = "mangle" -- chain = "%s_%s_%s" % (table, target, chain_suffix) -+ _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX) -+ chain = "%s_%s_%s" % (table, _policy, chain_suffix) - rule_action = {"mangle": {"key": {"meta": {"key": "mark"}}, - "value": rich_rule.action.set}} - else: -@@ -1121,10 +1117,10 @@ class nftables(object): - else: - return {"range": [range[0], range[1]]} - -- def build_zone_ports_rules(self, enable, zone, proto, port, destination=None, rich_rule=None): -+ def build_policy_ports_rules(self, enable, policy, proto, port, destination=None, rich_rule=None): - add_del = { True: "add", False: "delete" }[enable] - table = "filter" -- target = DEFAULT_ZONE_TARGET.format(chain=SHORTCUTS["INPUT"], zone=zone) -+ _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX) - - expr_fragments = [] - if rich_rule: -@@ -1146,21 +1142,21 @@ class nftables(object): - - rules = [] - if rich_rule: -- rules.append(self._rich_rule_log(rich_rule, enable, table, target, expr_fragments)) -- rules.append(self._rich_rule_audit(rich_rule, enable, table, target, expr_fragments)) -- rules.append(self._rich_rule_action(zone, rich_rule, enable, table, target, expr_fragments)) -+ rules.append(self._rich_rule_log(policy, rich_rule, enable, table, expr_fragments)) -+ rules.append(self._rich_rule_audit(policy, rich_rule, enable, table, expr_fragments)) -+ rules.append(self._rich_rule_action(policy, rich_rule, enable, table, expr_fragments)) - else: - rules.append({add_del: {"rule": {"family": "inet", - "table": TABLE_NAME, -- "chain": "%s_%s_allow" % (table, target), -+ "chain": "%s_%s_allow" % (table, _policy), - "expr": expr_fragments + [{"accept": None}]}}}) - - return rules - -- def build_zone_protocol_rules(self, enable, zone, protocol, destination=None, rich_rule=None): -+ def build_policy_protocol_rules(self, enable, policy, protocol, destination=None, rich_rule=None): - add_del = { True: "add", False: "delete" }[enable] - table = "filter" -- target = DEFAULT_ZONE_TARGET.format(chain=SHORTCUTS["INPUT"], zone=zone) -+ _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX) - - expr_fragments = [] - if rich_rule: -@@ -1181,22 +1177,22 @@ class nftables(object): - - rules = [] - if rich_rule: -- rules.append(self._rich_rule_log(rich_rule, enable, table, target, expr_fragments)) -- rules.append(self._rich_rule_audit(rich_rule, enable, table, target, expr_fragments)) -- rules.append(self._rich_rule_action(zone, rich_rule, enable, table, target, expr_fragments)) -+ rules.append(self._rich_rule_log(policy, rich_rule, enable, table, expr_fragments)) -+ rules.append(self._rich_rule_audit(policy, rich_rule, enable, table, expr_fragments)) -+ rules.append(self._rich_rule_action(policy, rich_rule, enable, table, expr_fragments)) - else: - rules.append({add_del: {"rule": {"family": "inet", - "table": TABLE_NAME, -- "chain": "%s_%s_allow" % (table, target), -+ "chain": "%s_%s_allow" % (table, _policy), - "expr": expr_fragments + [{"accept": None}]}}}) - - return rules - -- def build_zone_source_ports_rules(self, enable, zone, proto, port, -+ def build_policy_source_ports_rules(self, enable, policy, proto, port, - destination=None, rich_rule=None): - add_del = { True: "add", False: "delete" }[enable] - table = "filter" -- target = DEFAULT_ZONE_TARGET.format(chain=SHORTCUTS["INPUT"], zone=zone) -+ _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX) - - expr_fragments = [] - if rich_rule: -@@ -1218,19 +1214,21 @@ class nftables(object): - - rules = [] - if rich_rule: -- rules.append(self._rich_rule_log(rich_rule, enable, table, target, expr_fragments)) -- rules.append(self._rich_rule_audit(rich_rule, enable, table, target, expr_fragments)) -- rules.append(self._rich_rule_action(zone, rich_rule, enable, table, target, expr_fragments)) -+ rules.append(self._rich_rule_log(policy, rich_rule, enable, table, expr_fragments)) -+ rules.append(self._rich_rule_audit(policy, rich_rule, enable, table, expr_fragments)) -+ rules.append(self._rich_rule_action(policy, rich_rule, enable, table, expr_fragments)) - else: - rules.append({add_del: {"rule": {"family": "inet", - "table": TABLE_NAME, -- "chain": "%s_%s_allow" % (table, target), -+ "chain": "%s_%s_allow" % (table, _policy), - "expr": expr_fragments + [{"accept": None}]}}}) - - return rules - -- def build_zone_helper_ports_rules(self, enable, zone, proto, port, -- destination, helper_name, module_short_name): -+ def build_policy_helper_ports_rules(self, enable, policy, proto, port, -+ destination, helper_name, module_short_name): -+ table = "filter" -+ _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX) - add_del = { True: "add", False: "delete" }[enable] - rules = [] - -@@ -1241,8 +1239,6 @@ class nftables(object): - "type": module_short_name, - "protocol": proto}}}) - -- target = DEFAULT_ZONE_TARGET.format(chain=SHORTCUTS["INPUT"], -- zone=zone) - expr_fragments = [] - if destination: - expr_fragments.append(self._rule_addr_fragment("daddr", destination)) -@@ -1253,15 +1249,15 @@ class nftables(object): - expr_fragments.append({"ct helper": "helper-%s-%s" % (helper_name, proto)}) - rules.append({add_del: {"rule": {"family": "inet", - "table": TABLE_NAME, -- "chain": "filter_%s_allow" % (target), -+ "chain": "filter_%s_allow" % (_policy), - "expr": expr_fragments}}}) - - return rules - -- def _build_zone_masquerade_nat_rules(self, enable, zone, family, rich_rule=None): -+ def _build_policy_masquerade_nat_rules(self, enable, policy, family, rich_rule=None): -+ table = "nat" -+ _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX) - add_del = { True: "add", False: "delete" }[enable] -- target = DEFAULT_ZONE_TARGET.format(chain=SHORTCUTS["POSTROUTING"], -- zone=zone) - - expr_fragments = [] - if rich_rule: -@@ -1273,7 +1269,7 @@ class nftables(object): - - rule = {"family": family, - "table": TABLE_NAME, -- "chain": "nat_%s_%s" % (target, chain_suffix), -+ "chain": "nat_%s_%s" % (_policy, chain_suffix), - "expr": expr_fragments + - [{"match": {"left": {"meta": {"key": "oifname"}}, - "op": "!=", -@@ -1282,21 +1278,21 @@ class nftables(object): - rule.update(self._rich_rule_priority_fragment(rich_rule)) - return [{add_del: {"rule": rule}}] - -- def build_zone_masquerade_rules(self, enable, zone, rich_rule=None): -+ def build_policy_masquerade_rules(self, enable, policy, rich_rule=None): - # nat tables needs to use ip/ip6 family - rules = [] - if rich_rule and (rich_rule.family and rich_rule.family == "ipv6" - or rich_rule.source and check_address("ipv6", rich_rule.source.addr)): -- rules.extend(self._build_zone_masquerade_nat_rules(enable, zone, "ip6", rich_rule)) -+ rules.extend(self._build_policy_masquerade_nat_rules(enable, policy, "ip6", rich_rule)) - elif rich_rule and (rich_rule.family and rich_rule.family == "ipv4" - or rich_rule.source and check_address("ipv4", rich_rule.source.addr)): -- rules.extend(self._build_zone_masquerade_nat_rules(enable, zone, "ip", rich_rule)) -+ rules.extend(self._build_policy_masquerade_nat_rules(enable, policy, "ip", rich_rule)) - else: -- rules.extend(self._build_zone_masquerade_nat_rules(enable, zone, "ip", rich_rule)) -+ rules.extend(self._build_policy_masquerade_nat_rules(enable, policy, "ip", rich_rule)) - -+ table = "filter" -+ _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX) - add_del = { True: "add", False: "delete" }[enable] -- target = DEFAULT_ZONE_TARGET.format(chain=SHORTCUTS["FORWARD_OUT"], -- zone=zone) - - expr_fragments = [] - if rich_rule: -@@ -1308,7 +1304,7 @@ class nftables(object): - - rule = {"family": "inet", - "table": TABLE_NAME, -- "chain": "filter_%s_%s" % (target, chain_suffix), -+ "chain": "filter_%s_%s" % (_policy, chain_suffix), - "expr": expr_fragments + - [{"match": {"left": {"ct": {"key": "state"}}, - "op": "in", -@@ -1319,12 +1315,12 @@ class nftables(object): - - return rules - -- def _build_zone_forward_port_nat_rules(self, enable, zone, port, protocol, -+ def _build_policy_forward_port_nat_rules(self, enable, policy, port, protocol, - toaddr, toport, family, - rich_rule=None): -+ table = "nat" -+ _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX) - add_del = { True: "add", False: "delete" }[enable] -- target = DEFAULT_ZONE_TARGET.format(chain=SHORTCUTS["PREROUTING"], -- zone=zone) - - expr_fragments = [] - if rich_rule: -@@ -1351,28 +1347,28 @@ class nftables(object): - - rule = {"family": family, - "table": TABLE_NAME, -- "chain": "nat_%s_%s" % (target, chain_suffix), -+ "chain": "nat_%s_%s" % (_policy, chain_suffix), - "expr": expr_fragments} - rule.update(self._rich_rule_priority_fragment(rich_rule)) - return [{add_del: {"rule": rule}}] - -- def build_zone_forward_port_rules(self, enable, zone, port, -+ def build_policy_forward_port_rules(self, enable, policy, port, - protocol, toport, toaddr, rich_rule=None): - rules = [] - if rich_rule and (rich_rule.family and rich_rule.family == "ipv6" - or toaddr and check_single_address("ipv6", toaddr)): -- rules.extend(self._build_zone_forward_port_nat_rules(enable, zone, -+ rules.extend(self._build_policy_forward_port_nat_rules(enable, policy, - port, protocol, toaddr, toport, "ip6", rich_rule)) - elif rich_rule and (rich_rule.family and rich_rule.family == "ipv4" - or toaddr and check_single_address("ipv4", toaddr)): -- rules.extend(self._build_zone_forward_port_nat_rules(enable, zone, -+ rules.extend(self._build_policy_forward_port_nat_rules(enable, policy, - port, protocol, toaddr, toport, "ip", rich_rule)) - else: - if toaddr and check_single_address("ipv6", toaddr): -- rules.extend(self._build_zone_forward_port_nat_rules(enable, zone, -+ rules.extend(self._build_policy_forward_port_nat_rules(enable, policy, - port, protocol, toaddr, toport, "ip6", rich_rule)) - else: -- rules.extend(self._build_zone_forward_port_nat_rules(enable, zone, -+ rules.extend(self._build_policy_forward_port_nat_rules(enable, policy, - port, protocol, toaddr, toport, "ip", rich_rule)) - - return rules -@@ -1384,8 +1380,9 @@ class nftables(object): - raise FirewallError(INVALID_ICMPTYPE, - "ICMP type '%s' not supported by %s" % (icmp_type, self.name)) - -- def build_zone_icmp_block_rules(self, enable, zone, ict, rich_rule=None): -+ def build_policy_icmp_block_rules(self, enable, policy, ict, rich_rule=None): - table = "filter" -+ _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX) - add_del = { True: "add", False: "delete" }[enable] - - if rich_rule and rich_rule.ipvs: -@@ -1401,83 +1398,77 @@ class nftables(object): - - rules = [] - for ipv in ipvs: -- for chain in ["INPUT", "FORWARD_IN"]: -- target = DEFAULT_ZONE_TARGET.format(chain=SHORTCUTS[chain], -- zone=zone) -- if self._fw.zone.query_icmp_block_inversion(zone): -- final_chain = "%s_%s_allow" % (table, target) -- target_fragment = {"accept": None} -- else: -- final_chain = "%s_%s_deny" % (table, target) -- target_fragment = self._reject_fragment() -- -- expr_fragments = [] -- if rich_rule: -- expr_fragments.append(self._rich_rule_family_fragment(rich_rule.family)) -- expr_fragments.append(self._rich_rule_destination_fragment(rich_rule.destination)) -- expr_fragments.append(self._rich_rule_source_fragment(rich_rule.source)) -- expr_fragments.extend(self._icmp_types_to_nft_fragments(ipv, ict.name)) -- -- if rich_rule: -- rules.append(self._rich_rule_log(rich_rule, enable, table, target, expr_fragments)) -- rules.append(self._rich_rule_audit(rich_rule, enable, table, target, expr_fragments)) -- if rich_rule.action: -- rules.append(self._rich_rule_action(zone, rich_rule, enable, table, target, expr_fragments)) -- else: -- chain_suffix = self._rich_rule_chain_suffix(rich_rule) -- rule = {"family": "inet", -- "table": TABLE_NAME, -- "chain": "%s_%s_%s" % (table, target, chain_suffix), -- "expr": expr_fragments + [self._reject_fragment()]} -- rule.update(self._rich_rule_priority_fragment(rich_rule)) -- rules.append({add_del: {"rule": rule}}) -+ if self._fw.policy.query_icmp_block_inversion(policy): -+ final_chain = "%s_%s_allow" % (table, _policy) -+ target_fragment = {"accept": None} -+ else: -+ final_chain = "%s_%s_deny" % (table, _policy) -+ target_fragment = self._reject_fragment() -+ -+ expr_fragments = [] -+ if rich_rule: -+ expr_fragments.append(self._rich_rule_family_fragment(rich_rule.family)) -+ expr_fragments.append(self._rich_rule_destination_fragment(rich_rule.destination)) -+ expr_fragments.append(self._rich_rule_source_fragment(rich_rule.source)) -+ expr_fragments.extend(self._icmp_types_to_nft_fragments(ipv, ict.name)) -+ -+ if rich_rule: -+ rules.append(self._rich_rule_log(policy, rich_rule, enable, table, expr_fragments)) -+ rules.append(self._rich_rule_audit(policy, rich_rule, enable, table, expr_fragments)) -+ if rich_rule.action: -+ rules.append(self._rich_rule_action(policy, rich_rule, enable, table, expr_fragments)) - else: -- if self._fw.get_log_denied() != "off" and self._fw.zone.query_icmp_block_inversion(zone): -- rules.append({add_del: {"rule": {"family": "inet", -- "table": TABLE_NAME, -- "chain": final_chain, -- "expr": (expr_fragments + -- [self._pkttype_match_fragment(self._fw.get_log_denied()), -- {"log": {"prefix": "\"%s_%s_ICMP_BLOCK: \"" % (table, zone)}}])}}}) -+ chain_suffix = self._rich_rule_chain_suffix(rich_rule) -+ rule = {"family": "inet", -+ "table": TABLE_NAME, -+ "chain": "%s_%s_%s" % (table, _policy, chain_suffix), -+ "expr": expr_fragments + [self._reject_fragment()]} -+ rule.update(self._rich_rule_priority_fragment(rich_rule)) -+ rules.append({add_del: {"rule": rule}}) -+ else: -+ if self._fw.get_log_denied() != "off" and self._fw.policy.query_icmp_block_inversion(policy): - rules.append({add_del: {"rule": {"family": "inet", - "table": TABLE_NAME, - "chain": final_chain, -- "expr": expr_fragments + [target_fragment]}}}) -+ "expr": (expr_fragments + -+ [self._pkttype_match_fragment(self._fw.get_log_denied()), -+ {"log": {"prefix": "\"%s_%s_ICMP_BLOCK: \"" % (table, policy)}}])}}}) -+ rules.append({add_del: {"rule": {"family": "inet", -+ "table": TABLE_NAME, -+ "chain": final_chain, -+ "expr": expr_fragments + [target_fragment]}}}) - - return rules - -- def build_zone_icmp_block_inversion_rules(self, enable, zone): -+ def build_policy_icmp_block_inversion_rules(self, enable, policy): - table = "filter" -+ _policy = self._fw.policy.policy_base_chain_name(policy, table, POLICY_CHAIN_PREFIX) - rules = [] - add_del = { True: "add", False: "delete" }[enable] - -- for chain in ["INPUT", "FORWARD_IN"]: -- _zone = DEFAULT_ZONE_TARGET.format(chain=SHORTCUTS[chain], -- zone=zone) -+ if self._fw.policy.query_icmp_block_inversion(policy): -+ target_fragment = self._reject_fragment() -+ else: -+ target_fragment = {"accept": None} - -- if self._fw.zone.query_icmp_block_inversion(zone): -- target_fragment = self._reject_fragment() -- else: -- target_fragment = {"accept": None} -+ # WARN: The "index" used here must be kept in sync with -+ # build_policy_chain_rules() -+ # -+ rules.append({add_del: {"rule": {"family": "inet", -+ "table": TABLE_NAME, -+ "chain": "%s_%s" % (table, _policy), -+ "index": 4, -+ "expr": [self._icmp_match_fragment(), -+ target_fragment]}}}) - -- # WARN: The "index" used here must be kept in sync with -- # build_zone_chain_rules() -- # -+ if self._fw.get_log_denied() != "off" and self._fw.policy.query_icmp_block_inversion(policy): - rules.append({add_del: {"rule": {"family": "inet", - "table": TABLE_NAME, -- "chain": "%s_%s" % (table, _zone), -+ "chain": "%s_%s" % (table, _policy), - "index": 4, - "expr": [self._icmp_match_fragment(), -- target_fragment]}}}) -- -- if self._fw.get_log_denied() != "off" and self._fw.zone.query_icmp_block_inversion(zone): -- rules.append({add_del: {"rule": {"family": "inet", -- "table": TABLE_NAME, -- "chain": "%s_%s" % (table, _zone), -- "index": 4, -- "expr": [self._icmp_match_fragment(), -- self._pkttype_match_fragment(self._fw.get_log_denied()), -- {"log": {"prefix": "%s_%s_ICMP_BLOCK: " % (table, _zone)}}]}}}) -+ self._pkttype_match_fragment(self._fw.get_log_denied()), -+ {"log": {"prefix": "%s_%s_ICMP_BLOCK: " % (table, policy)}}]}}}) - return rules - - def build_rpfilter_rules(self, log_denied=False): -@@ -1543,10 +1534,8 @@ class nftables(object): - "expr": expr_fragments}}}) - return rules - -- def build_zone_rich_source_destination_rules(self, enable, zone, rich_rule): -+ def build_policy_rich_source_destination_rules(self, enable, policy, rich_rule): - table = "filter" -- target = DEFAULT_ZONE_TARGET.format(chain=SHORTCUTS["INPUT"], -- zone=zone) - - expr_fragments = [] - expr_fragments.append(self._rich_rule_family_fragment(rich_rule.family)) -@@ -1554,9 +1543,9 @@ class nftables(object): - expr_fragments.append(self._rich_rule_source_fragment(rich_rule.source)) - - rules = [] -- rules.append(self._rich_rule_log(rich_rule, enable, table, target, expr_fragments)) -- rules.append(self._rich_rule_audit(rich_rule, enable, table, target, expr_fragments)) -- rules.append(self._rich_rule_action(zone, rich_rule, enable, table, target, expr_fragments)) -+ rules.append(self._rich_rule_log(policy, rich_rule, enable, table, expr_fragments)) -+ rules.append(self._rich_rule_audit(policy, rich_rule, enable, table, expr_fragments)) -+ rules.append(self._rich_rule_action(policy, rich_rule, enable, table, expr_fragments)) - - return rules - -diff --git a/src/firewall/errors.py b/src/firewall/errors.py -index 4589f60..2ad46c2 100644 ---- a/src/firewall/errors.py -+++ b/src/firewall/errors.py -@@ -88,6 +88,7 @@ INVALID_ENTRY = 136 - INVALID_OPTION = 137 - INVALID_HELPER = 138 - INVALID_PRIORITY = 139 -+INVALID_POLICY = 140 - - MISSING_TABLE = 200 - MISSING_CHAIN = 201 -diff --git a/src/firewall/functions.py b/src/firewall/functions.py -index 6bc52d9..d4c5e90 100644 ---- a/src/firewall/functions.py -+++ b/src/firewall/functions.py -@@ -27,7 +27,7 @@ __all__ = [ "PY2", "getPortID", "getPortRange", "portStr", "getServiceName", - "check_single_address", "check_mac", "uniqify", "ppid_of_pid", - "max_zone_name_len", "checkUser", "checkUid", "checkCommand", - "checkContext", "joinArgs", "splitArgs", -- "b2u", "u2b", "u2b_if_py2" ] -+ "b2u", "u2b", "u2b_if_py2", "max_policy_name_len"] - - import socket - import os -@@ -505,6 +505,15 @@ def ppid_of_pid(pid): - return None - return pid - -+def max_policy_name_len(): -+ """ -+ iptables limits length of chain to (currently) 28 chars. -+ The longest chain we create is pol__allow, -+ which leaves 28 - 10 = 18 chars for . -+ """ -+ from firewall.core.ipXtables import POLICY_CHAIN_PREFIX -+ return 28 - (len(POLICY_CHAIN_PREFIX) + len("_allow")) -+ - def max_zone_name_len(): - """ - Netfilter limits length of chain to (currently) 28 chars. -diff --git a/src/tests/features/service_include.at b/src/tests/features/service_include.at -index 7f02701..1c86900 100644 ---- a/src/tests/features/service_include.at -+++ b/src/tests/features/service_include.at -@@ -116,9 +116,7 @@ FWD_CHECK([-q --permanent --zone=drop --add-interface=foobar0]) - FWD_CHECK([-q --permanent --zone=drop --add-service=my-service-with-include]) - FWD_CHECK([-q --permanent --service=my-service-with-include --add-include=does-not-exist]) - FWD_RELOAD(101, [ignore], [ignore], 251) --FWD_CHECK([--zone=drop --list-services], 0, [dnl - --]) - FWD_CHECK([--zone=public --list-services], 0, [dnl - dhcpv6-client ssh - ]) --- -1.8.3.1 - diff --git a/0001-fix-build-distribute-new-python-files.patch b/0001-fix-build-distribute-new-python-files.patch deleted file mode 100644 index 6cdd58f2ed8fc668f85b8197d3403815d173915c..0000000000000000000000000000000000000000 --- a/0001-fix-build-distribute-new-python-files.patch +++ /dev/null @@ -1,35 +0,0 @@ -From e8714cb5e3ad20708b3d481d51c3aa26c04a52d3 Mon Sep 17 00:00:00 2001 -From: Eric Garver -Date: Mon, 6 Apr 2020 16:52:02 -0400 -Subject: [PATCH] fix: build: distribute new python files - -Make sure we distribute the new python files. - -Fixes: 34bdee40aa61 ("feat: implement policy objects internally") ---- - src/Makefile.am | 2 ++ - 1 file changed, 2 insertions(+) - -diff --git a/src/Makefile.am b/src/Makefile.am -index 76589d6..985c46a 100644 ---- a/src/Makefile.am -+++ b/src/Makefile.am -@@ -27,6 +27,7 @@ nobase_dist_python_DATA = \ - firewall/core/fw_ipset.py \ - firewall/core/fw_nm.py \ - firewall/core/fw_policies.py \ -+ firewall/core/fw_policy.py \ - firewall/core/fw.py \ - firewall/core/fw_service.py \ - firewall/core/fw_transaction.py \ -@@ -44,6 +45,7 @@ nobase_dist_python_DATA = \ - firewall/core/io/io_object.py \ - firewall/core/io/ipset.py \ - firewall/core/io/lockdown_whitelist.py \ -+ firewall/core/io/policy.py \ - firewall/core/io/service.py \ - firewall/core/io/zone.py \ - firewall/core/ipset.py \ --- -1.8.3.1 - diff --git a/0001-fix-po-add-new-python-files-to-POTFILES.patch b/0001-fix-po-add-new-python-files-to-POTFILES.patch deleted file mode 100644 index 8df6fc8867d6760ac3bc7468affbaf9385f69436..0000000000000000000000000000000000000000 --- a/0001-fix-po-add-new-python-files-to-POTFILES.patch +++ /dev/null @@ -1,33 +0,0 @@ -From 1bac8783de46896b54161d8fe3cdbe7d1d7a1446 Mon Sep 17 00:00:00 2001 -From: Eric Garver -Date: Wed, 8 Apr 2020 14:42:14 -0400 -Subject: [PATCH] fix: po: add new python files to POTFILES - -Fixes: 34bdee40aa61 ("feat: implement policy objects internally") ---- - po/POTFILES.in | 2 ++ - 1 file changed, 2 insertions(+) - -diff --git a/po/POTFILES.in b/po/POTFILES.in -index 918f6f0..56952fe 100644 ---- a/po/POTFILES.in -+++ b/po/POTFILES.in -@@ -242,6 +242,7 @@ src/firewall/core/fw_ifcfg.py - src/firewall/core/fw_ipset.py - src/firewall/core/fw_nm.py - src/firewall/core/fw_policies.py -+src/firewall/core/fw_policy.py - src/firewall/core/fw.py - src/firewall/core/fw_service.py - src/firewall/core/fw_transaction.py -@@ -259,6 +260,7 @@ src/firewall/core/io/__init__.py - src/firewall/core/io/io_object.py - src/firewall/core/io/ipset.py - src/firewall/core/io/lockdown_whitelist.py -+src/firewall/core/io/policy.py - src/firewall/core/io/service.py - src/firewall/core/io/zone.py - src/firewall/core/ipset.py --- -1.8.3.1 - diff --git a/0001-fix-policy-ipXtables-calculate-max-name-len-properly.patch b/0001-fix-policy-ipXtables-calculate-max-name-len-properly.patch deleted file mode 100644 index 55a15bbbc6c99f9a7d5ad1864438d1fb3b6bd3f2..0000000000000000000000000000000000000000 --- a/0001-fix-policy-ipXtables-calculate-max-name-len-properly.patch +++ /dev/null @@ -1,54 +0,0 @@ -From c6fe749fb75004c30818bcc0696ac23801239d0b Mon Sep 17 00:00:00 2001 -From: Eric Garver -Date: Tue, 21 Jul 2020 16:03:24 -0400 -Subject: [PATCH] fix(policy): ipXtables: calculate max name len properly - -Policy chain names still need the SHORTCUTS (POST, IN, etc) in the chain -name. As such, calculate the max name length appropriately. - -This also drops the "pol_" prefix for policy chains. Retaining it would -restrict the policy name max length unreasonably so. - -Fixes: 34bdee40aa61 ("feat: implement policy objects internally") ---- - src/firewall/core/ipXtables.py | 2 +- - src/firewall/functions.py | 8 +++++--- - 2 files changed, 6 insertions(+), 4 deletions(-) - -diff --git a/src/firewall/core/ipXtables.py b/src/firewall/core/ipXtables.py -index b310a74..54c267b 100644 ---- a/src/firewall/core/ipXtables.py -+++ b/src/firewall/core/ipXtables.py -@@ -32,7 +32,7 @@ from firewall.core.rich import Rich_Accept, Rich_Reject, Rich_Drop, Rich_Mark, \ - Rich_Masquerade, Rich_ForwardPort, Rich_IcmpBlock - import string - --POLICY_CHAIN_PREFIX = "pol_" -+POLICY_CHAIN_PREFIX = "" - - BUILT_IN_CHAINS = { - "security": [ "INPUT", "OUTPUT", "FORWARD" ], -diff --git a/src/firewall/functions.py b/src/firewall/functions.py -index d4c5e90..de4e244 100644 ---- a/src/firewall/functions.py -+++ b/src/firewall/functions.py -@@ -508,11 +508,13 @@ def ppid_of_pid(pid): - def max_policy_name_len(): - """ - iptables limits length of chain to (currently) 28 chars. -- The longest chain we create is pol__allow, -- which leaves 28 - 10 = 18 chars for . -+ The longest chain we create is POST__allow, -+ which leaves 28 - 11 = 17 chars for . - """ - from firewall.core.ipXtables import POLICY_CHAIN_PREFIX -- return 28 - (len(POLICY_CHAIN_PREFIX) + len("_allow")) -+ from firewall.core.base import SHORTCUTS -+ longest_shortcut = max(map(len, SHORTCUTS.values())) -+ return 28 - (longest_shortcut + len(POLICY_CHAIN_PREFIX) + len("_allow")) - - def max_zone_name_len(): - """ --- -1.8.3.1 - diff --git a/0001-fix-zone-listing-rich-rules-in-default-zone.patch b/0001-fix-zone-listing-rich-rules-in-default-zone.patch deleted file mode 100644 index c2cbea3818aa9a44796d70701f2f564aa435a6fb..0000000000000000000000000000000000000000 --- a/0001-fix-zone-listing-rich-rules-in-default-zone.patch +++ /dev/null @@ -1,25 +0,0 @@ -From 3d418e35afecf68ba955915f29a003ad81258037 Mon Sep 17 00:00:00 2001 -From: Eric Garver -Date: Tue, 28 Apr 2020 13:48:53 -0400 -Subject: [PATCH] fix(zone): listing rich rules in default zone - -Fixes: 34bdee40aa61 ("feat: implement policy objects internally") ---- - src/firewall/core/fw_zone.py | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/src/firewall/core/fw_zone.py b/src/firewall/core/fw_zone.py -index 6f6fba0..7048a90 100644 ---- a/src/firewall/core/fw_zone.py -+++ b/src/firewall/core/fw_zone.py -@@ -744,6 +744,7 @@ class FirewallZone(object): - return ret - - def list_rules(self, zone): -+ zone = self._fw.check_zone(zone) - ret = set() - for p_name in [self.policy_name_from_zones(zone, "ANY"), - self.policy_name_from_zones(zone, "HOST"), --- -1.8.3.1 - diff --git a/0001-improvement-port-allow-coalescing-and-breaking-of-ra.patch b/0001-improvement-port-allow-coalescing-and-breaking-of-ra.patch deleted file mode 100644 index 0147339b59dedabc5d39065bcf92732dbaffb846..0000000000000000000000000000000000000000 --- a/0001-improvement-port-allow-coalescing-and-breaking-of-ra.patch +++ /dev/null @@ -1,390 +0,0 @@ -From a5291bcee84b56b30aac38544d85fb601fe6a25a Mon Sep 17 00:00:00 2001 -From: Eric Garver -Date: Tue, 17 Mar 2020 13:51:43 -0400 -Subject: [PATCH] improvement: port: allow coalescing and breaking of ranges - ---- - src/firewall/core/fw_zone.py | 106 +++++++++++++++++++++++++++---------- - src/firewall/functions.py | 85 +++++++++++++++++++++++++++++ - src/firewall/server/config_zone.py | 55 ++++++++++++++----- - 3 files changed, 204 insertions(+), 42 deletions(-) - -diff --git a/src/firewall/core/fw_zone.py b/src/firewall/core/fw_zone.py -index 2bc94e3..d32d7a8 100644 ---- a/src/firewall/core/fw_zone.py -+++ b/src/firewall/core/fw_zone.py -@@ -25,7 +25,7 @@ from firewall.core.base import SHORTCUTS, DEFAULT_ZONE_TARGET, \ - from firewall.core.logger import log - from firewall.functions import portStr, checkIPnMask, checkIP6nMask, \ - checkProtocol, enable_ip_forwarding, check_single_address, check_mac, \ -- portInPortRange, get_nf_conntrack_short_name -+ portInPortRange, get_nf_conntrack_short_name, coalescePortRange, breakPortRange - from firewall.core.rich import Rich_Rule, Rich_Accept, \ - Rich_Mark, Rich_Service, Rich_Port, Rich_Protocol, \ - Rich_Masquerade, Rich_ForwardPort, Rich_SourcePort, Rich_IcmpBlock, \ -@@ -857,11 +857,13 @@ class FirewallZone(object): - self._fw.check_panic() - _obj = self._zones[_zone] - -- port_id = self.__port_id(port, protocol) -- if port_id in _obj.settings["ports"]: -- raise FirewallError(errors.ALREADY_ENABLED, -- "'%s:%s' already in '%s'" % (port, protocol, -- _zone)) -+ existing_port_ids = list(filter(lambda x: x[1] == protocol, _obj.settings["ports"])) -+ for port_id in existing_port_ids: -+ if portInPortRange(port, port_id[0]): -+ raise FirewallError(errors.ALREADY_ENABLED, -+ "'%s:%s' already in '%s'" % (port, protocol, _zone)) -+ -+ added_ranges, removed_ranges = coalescePortRange(port, [_port for (_port, _protocol) in existing_port_ids]) - - if use_transaction is None: - transaction = self.new_transaction() -@@ -869,10 +871,18 @@ class FirewallZone(object): - transaction = use_transaction - - if _obj.applied: -- self._port(True, _zone, port, protocol, transaction) -- -- self.__register_port(_obj, port_id, timeout, sender) -- transaction.add_fail(self.__unregister_port, _obj, port_id) -+ for range in added_ranges: -+ self._port(True, _zone, portStr(range, "-"), protocol, transaction) -+ for range in removed_ranges: -+ self._port(False, _zone, portStr(range, "-"), protocol, transaction) -+ -+ for range in added_ranges: -+ port_id = self.__port_id(range, protocol) -+ self.__register_port(_obj, port_id, timeout, sender) -+ transaction.add_fail(self.__unregister_port, _obj, port_id) -+ for range in removed_ranges: -+ port_id = self.__port_id(range, protocol) -+ transaction.add_post(self.__unregister_port, _obj, port_id) - - if use_transaction is None: - transaction.execute(True) -@@ -889,20 +899,34 @@ class FirewallZone(object): - self._fw.check_panic() - _obj = self._zones[_zone] - -- port_id = self.__port_id(port, protocol) -- if port_id not in _obj.settings["ports"]: -+ existing_port_ids = list(filter(lambda x: x[1] == protocol, _obj.settings["ports"])) -+ for port_id in existing_port_ids: -+ if portInPortRange(port, port_id[0]): -+ break -+ else: - raise FirewallError(errors.NOT_ENABLED, - "'%s:%s' not in '%s'" % (port, protocol, _zone)) - -+ added_ranges, removed_ranges = breakPortRange(port, [_port for (_port, _protocol) in existing_port_ids]) -+ - if use_transaction is None: - transaction = self.new_transaction() - else: - transaction = use_transaction - - if _obj.applied: -- self._port(False, _zone, port, protocol, transaction) -- -- transaction.add_post(self.__unregister_port, _obj, port_id) -+ for range in added_ranges: -+ self._port(True, _zone, portStr(range, "-"), protocol, transaction) -+ for range in removed_ranges: -+ self._port(False, _zone, portStr(range, "-"), protocol, transaction) -+ -+ for range in added_ranges: -+ port_id = self.__port_id(range, protocol) -+ self.__register_port(_obj, port_id, 0, None) -+ transaction.add_fail(self.__unregister_port, _obj, port_id) -+ for range in removed_ranges: -+ port_id = self.__port_id(range, protocol) -+ transaction.add_post(self.__unregister_port, _obj, port_id) - - if use_transaction is None: - transaction.execute(True) -@@ -1015,11 +1039,13 @@ class FirewallZone(object): - self._fw.check_panic() - _obj = self._zones[_zone] - -- port_id = self.__source_port_id(port, protocol) -- if port_id in _obj.settings["source_ports"]: -- raise FirewallError(errors.ALREADY_ENABLED, -- "'%s:%s' already in '%s'" % (port, protocol, -- _zone)) -+ existing_port_ids = list(filter(lambda x: x[1] == protocol, _obj.settings["source_ports"])) -+ for port_id in existing_port_ids: -+ if portInPortRange(port, port_id[0]): -+ raise FirewallError(errors.ALREADY_ENABLED, -+ "'%s:%s' already in '%s'" % (port, protocol, _zone)) -+ -+ added_ranges, removed_ranges = coalescePortRange(port, [_port for (_port, _protocol) in existing_port_ids]) - - if use_transaction is None: - transaction = self.new_transaction() -@@ -1027,10 +1053,18 @@ class FirewallZone(object): - transaction = use_transaction - - if _obj.applied: -- self._source_port(True, _zone, port, protocol, transaction) -- -- self.__register_source_port(_obj, port_id, timeout, sender) -- transaction.add_fail(self.__unregister_source_port, _obj, port_id) -+ for range in added_ranges: -+ self._source_port(True, _zone, portStr(range, "-"), protocol, transaction) -+ for range in removed_ranges: -+ self._source_port(False, _zone, portStr(range, "-"), protocol, transaction) -+ -+ for range in added_ranges: -+ port_id = self.__source_port_id(range, protocol) -+ self.__register_source_port(_obj, port_id, timeout, sender) -+ transaction.add_fail(self.__unregister_source_port, _obj, port_id) -+ for range in removed_ranges: -+ port_id = self.__source_port_id(range, protocol) -+ transaction.add_post(self.__unregister_source_port, _obj, port_id) - - if use_transaction is None: - transaction.execute(True) -@@ -1047,20 +1081,34 @@ class FirewallZone(object): - self._fw.check_panic() - _obj = self._zones[_zone] - -- port_id = self.__source_port_id(port, protocol) -- if port_id not in _obj.settings["source_ports"]: -+ existing_port_ids = list(filter(lambda x: x[1] == protocol, _obj.settings["source_ports"])) -+ for port_id in existing_port_ids: -+ if portInPortRange(port, port_id[0]): -+ break -+ else: - raise FirewallError(errors.NOT_ENABLED, - "'%s:%s' not in '%s'" % (port, protocol, _zone)) - -+ added_ranges, removed_ranges = breakPortRange(port, [_port for (_port, _protocol) in existing_port_ids]) -+ - if use_transaction is None: - transaction = self.new_transaction() - else: - transaction = use_transaction - - if _obj.applied: -- self._source_port(False, _zone, port, protocol, transaction) -- -- transaction.add_post(self.__unregister_source_port, _obj, port_id) -+ for range in added_ranges: -+ self._source_port(True, _zone, portStr(range, "-"), protocol, transaction) -+ for range in removed_ranges: -+ self._source_port(False, _zone, portStr(range, "-"), protocol, transaction) -+ -+ for range in added_ranges: -+ port_id = self.__source_port_id(range, protocol) -+ self.__register_source_port(_obj, port_id, 0, None) -+ transaction.add_fail(self.__unregister_source_port, _obj, port_id) -+ for range in removed_ranges: -+ port_id = self.__source_port_id(range, protocol) -+ transaction.add_post(self.__unregister_source_port, _obj, port_id) - - if use_transaction is None: - transaction.execute(True) -diff --git a/src/firewall/functions.py b/src/firewall/functions.py -index 6af2206..6bc52d9 100644 ---- a/src/firewall/functions.py -+++ b/src/firewall/functions.py -@@ -72,6 +72,10 @@ def getPortRange(ports): - @return Array containing start and end port id for a valid range or -1 if port can not be found and -2 if port is too big for integer input or -1 for invalid ranges or None if the range is ambiguous. - """ - -+ # (port, port) or [port, port] case -+ if isinstance(ports, tuple) or isinstance(ports, list): -+ return ports -+ - # "" case - if isinstance(ports, int) or ports.isdigit(): - id1 = getPortID(ports) -@@ -155,6 +159,87 @@ def portInPortRange(port, range): - - return False - -+def coalescePortRange(new_range, ranges): -+ """ Coalesce a port range with existing list of port ranges -+ -+ @param new_range tuple/list/string -+ @param ranges list of tuple/list/string -+ @return tuple of (list of ranges added after coalescing, list of removed original ranges) -+ """ -+ -+ coalesced_range = getPortRange(new_range) -+ # normalize singleton ranges, e.g. (x,) --> (x,x) -+ if len(coalesced_range) == 1: -+ coalesced_range = (coalesced_range[0], coalesced_range[0]) -+ _ranges = map(getPortRange, ranges) -+ _ranges = sorted(map(lambda x: (x[0],x[0]) if len(x) == 1 else x, _ranges), key=lambda x: x[0]) -+ -+ removed_ranges = [] -+ for range in _ranges: -+ if coalesced_range[0] <= range[0] and coalesced_range[1] >= range[1]: -+ # new range covers this -+ removed_ranges.append(range) -+ elif coalesced_range[0] <= range[0] and coalesced_range[1] < range[1] and \ -+ coalesced_range[1] >= range[0]: -+ # expand beginning of range -+ removed_ranges.append(range) -+ coalesced_range = (coalesced_range[0], range[1]) -+ elif coalesced_range[0] > range[0] and coalesced_range[1] >= range[1] and \ -+ coalesced_range[0] <= range[1]: -+ # expand end of range -+ removed_ranges.append(range) -+ coalesced_range = (range[0], coalesced_range[1]) -+ -+ # normalize singleton ranges, e.g. (x,x) --> (x,) -+ removed_ranges = list(map(lambda x: (x[0],) if x[0] == x[1] else x, removed_ranges)) -+ if coalesced_range[0] == coalesced_range[1]: -+ coalesced_range = (coalesced_range[0],) -+ -+ return ([coalesced_range], removed_ranges) -+ -+def breakPortRange(remove_range, ranges): -+ """ break a port range from existing list of port ranges -+ -+ @param remove_range tuple/list/string -+ @param ranges list of tuple/list/string -+ @return tuple of (list of ranges added after breaking up, list of removed original ranges) -+ """ -+ -+ remove_range = getPortRange(remove_range) -+ # normalize singleton ranges, e.g. (x,) --> (x,x) -+ if len(remove_range) == 1: -+ remove_range = (remove_range[0], remove_range[0]) -+ _ranges = map(getPortRange, ranges) -+ _ranges = sorted(map(lambda x: (x[0],x[0]) if len(x) == 1 else x, _ranges), key=lambda x: x[0]) -+ -+ removed_ranges = [] -+ added_ranges = [] -+ for range in _ranges: -+ if remove_range[0] <= range[0] and remove_range[1] >= range[1]: -+ # remove entire range -+ removed_ranges.append(range) -+ elif remove_range[0] <= range[0] and remove_range[1] < range[1] and \ -+ remove_range[1] >= range[0]: -+ # remove from beginning of range -+ removed_ranges.append(range) -+ added_ranges.append((remove_range[1] + 1, range[1])) -+ elif remove_range[0] > range[0] and remove_range[1] >= range[1] and \ -+ remove_range[0] <= range[1]: -+ # remove from end of range -+ removed_ranges.append(range) -+ added_ranges.append((range[0], remove_range[0] - 1)) -+ elif remove_range[0] > range[0] and remove_range[1] < range[1]: -+ # remove inside range -+ removed_ranges.append(range) -+ added_ranges.append((range[0], remove_range[0] - 1)) -+ added_ranges.append((remove_range[1] + 1, range[1])) -+ -+ # normalize singleton ranges, e.g. (x,x) --> (x,) -+ removed_ranges = list(map(lambda x: (x[0],) if x[0] == x[1] else x, removed_ranges)) -+ added_ranges = list(map(lambda x: (x[0],) if x[0] == x[1] else x, added_ranges)) -+ -+ return (added_ranges, removed_ranges) -+ - def getServiceName(port, proto): - """ Check and Get service name from port and proto string combination using socket.getservbyport - -diff --git a/src/firewall/server/config_zone.py b/src/firewall/server/config_zone.py -index 1ae20ce..1c05318 100644 ---- a/src/firewall/server/config_zone.py -+++ b/src/firewall/server/config_zone.py -@@ -41,7 +41,8 @@ from firewall.server.decorators import handle_exceptions, \ - dbus_handle_exceptions, dbus_service_method - from firewall import errors - from firewall.errors import FirewallError --from firewall.functions import portInPortRange -+from firewall.functions import portStr, portInPortRange, coalescePortRange, \ -+ breakPortRange - - ############################################################################ - # -@@ -455,10 +456,16 @@ class FirewallDConfigZone(slip.dbus.service.Object): - protocol) - self.parent.accessCheck(sender) - settings = list(self.getSettings()) -- if (port,protocol) in settings[6]: -- raise FirewallError(errors.ALREADY_ENABLED, -- "%s:%s" % (port, protocol)) -- settings[6].append((port,protocol)) -+ existing_port_ids = list(filter(lambda x: x[1] == protocol, settings[6])) -+ for port_id in existing_port_ids: -+ if portInPortRange(port, port_id[0]): -+ raise FirewallError(errors.ALREADY_ENABLED, -+ "%s:%s" % (port, protocol)) -+ added_ranges, removed_ranges = coalescePortRange(port, [_port for (_port, _protocol) in existing_port_ids]) -+ for range in removed_ranges: -+ settings[6].remove((portStr(range, "-"), protocol)) -+ for range in added_ranges: -+ settings[6].append((portStr(range, "-"), protocol)) - self.update(settings) - - @dbus_service_method(config.dbus.DBUS_INTERFACE_CONFIG_ZONE, -@@ -471,9 +478,17 @@ class FirewallDConfigZone(slip.dbus.service.Object): - protocol) - self.parent.accessCheck(sender) - settings = list(self.getSettings()) -- if (port,protocol) not in settings[6]: -+ existing_port_ids = list(filter(lambda x: x[1] == protocol, settings[6])) -+ for port_id in existing_port_ids: -+ if portInPortRange(port, port_id[0]): -+ break -+ else: - raise FirewallError(errors.NOT_ENABLED, "%s:%s" % (port, protocol)) -- settings[6].remove((port,protocol)) -+ added_ranges, removed_ranges = breakPortRange(port, [_port for (_port, _protocol) in existing_port_ids]) -+ for range in removed_ranges: -+ settings[6].remove((portStr(range, "-"), protocol)) -+ for range in added_ranges: -+ settings[6].append((portStr(range, "-"), protocol)) - self.update(settings) - - @dbus_service_method(config.dbus.DBUS_INTERFACE_CONFIG_ZONE, -@@ -583,10 +598,16 @@ class FirewallDConfigZone(slip.dbus.service.Object): - protocol) - self.parent.accessCheck(sender) - settings = list(self.getSettings()) -- if (port,protocol) in settings[14]: -- raise FirewallError(errors.ALREADY_ENABLED, -- "%s:%s" % (port, protocol)) -- settings[14].append((port,protocol)) -+ existing_port_ids = list(filter(lambda x: x[1] == protocol, settings[14])) -+ for port_id in existing_port_ids: -+ if portInPortRange(port, port_id[0]): -+ raise FirewallError(errors.ALREADY_ENABLED, -+ "%s:%s" % (port, protocol)) -+ added_ranges, removed_ranges = coalescePortRange(port, [_port for (_port, _protocol) in existing_port_ids]) -+ for range in removed_ranges: -+ settings[14].remove((portStr(range, "-"), protocol)) -+ for range in added_ranges: -+ settings[14].append((portStr(range, "-"), protocol)) - self.update(settings) - - @dbus_service_method(config.dbus.DBUS_INTERFACE_CONFIG_ZONE, -@@ -599,9 +620,17 @@ class FirewallDConfigZone(slip.dbus.service.Object): - protocol) - self.parent.accessCheck(sender) - settings = list(self.getSettings()) -- if (port,protocol) not in settings[14]: -+ existing_port_ids = list(filter(lambda x: x[1] == protocol, settings[14])) -+ for port_id in existing_port_ids: -+ if portInPortRange(port, port_id[0]): -+ break -+ else: - raise FirewallError(errors.NOT_ENABLED, "%s:%s" % (port, protocol)) -- settings[14].remove((port,protocol)) -+ added_ranges, removed_ranges = breakPortRange(port, [_port for (_port, _protocol) in existing_port_ids]) -+ for range in removed_ranges: -+ settings[14].remove((portStr(range, "-"), protocol)) -+ for range in added_ranges: -+ settings[14].append((portStr(range, "-"), protocol)) - self.update(settings) - - @dbus_service_method(config.dbus.DBUS_INTERFACE_CONFIG_ZONE, --- -1.8.3.1 - diff --git a/0001-improvement-port-simplify-queryPort.patch b/0001-improvement-port-simplify-queryPort.patch deleted file mode 100644 index 74a3745c320ef419dff30c4bcd86945aba94e140..0000000000000000000000000000000000000000 --- a/0001-improvement-port-simplify-queryPort.patch +++ /dev/null @@ -1,55 +0,0 @@ -From cd8e0c3774a6c7ca6679fd50a0fb6f211528d9cc Mon Sep 17 00:00:00 2001 -From: Eric Garver -Date: Thu, 19 Mar 2020 16:22:18 -0400 -Subject: [PATCH] improvement: port: simplify queryPort - ---- - src/firewall/core/fw_zone.py | 10 +++------- - src/firewall/server/config_zone.py | 10 +++------- - 2 files changed, 6 insertions(+), 14 deletions(-) - -diff --git a/src/firewall/core/fw_zone.py b/src/firewall/core/fw_zone.py -index 5cda560..59d7a44 100644 ---- a/src/firewall/core/fw_zone.py -+++ b/src/firewall/core/fw_zone.py -@@ -914,13 +914,9 @@ class FirewallZone(object): - del _obj.settings["ports"][port_id] - - def query_port(self, zone, port, protocol): -- if self.__port_id(port, protocol) in self.get_settings(zone)["ports"]: -- return True -- else: -- # It might be a single port query that is inside a range -- for (_port, _protocol) in self.get_settings(zone)["ports"]: -- if portInPortRange(port, _port) and protocol == _protocol: -- return True -+ for (_port, _protocol) in self.get_settings(zone)["ports"]: -+ if portInPortRange(port, _port) and protocol == _protocol: -+ return True - - return False - -diff --git a/src/firewall/server/config_zone.py b/src/firewall/server/config_zone.py -index ed4eaba..bbbe7b5 100644 ---- a/src/firewall/server/config_zone.py -+++ b/src/firewall/server/config_zone.py -@@ -484,13 +484,9 @@ class FirewallDConfigZone(slip.dbus.service.Object): - protocol = dbus_to_python(protocol, str) - log.debug1("%s.queryPort('%s', '%s')", self._log_prefix, port, - protocol) -- if (port,protocol) in self.getSettings()[6]: -- return True -- else: -- # It might be a single port query that is inside a range -- for (_port, _protocol) in self.getSettings()[6]: -- if portInPortRange(port, _port) and protocol == _protocol: -- return True -+ for (_port, _protocol) in self.getSettings()[6]: -+ if portInPortRange(port, _port) and protocol == _protocol: -+ return True - - return False - --- -1.8.3.1 - diff --git a/firewalld-0.8.3.tar.gz b/firewalld-0.8.3.tar.gz deleted file mode 100644 index 10da022f156d915c21cbb41e42e39faff150fa7b..0000000000000000000000000000000000000000 Binary files a/firewalld-0.8.3.tar.gz and /dev/null differ diff --git a/firewalld-0.9.4.tar.gz b/firewalld-0.9.4.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..03957172e4c584f67e6c562724e3ce73b44cc2a5 Binary files /dev/null and b/firewalld-0.9.4.tar.gz differ diff --git a/firewalld.spec b/firewalld.spec index 770a008d2a5c79e9917686ad25f6637d2a941485..25b186825fd7c93b3bdaa79c29c1e70ebee9d501 100644 --- a/firewalld.spec +++ b/firewalld.spec @@ -1,6 +1,6 @@ Name: firewalld -Version: 0.8.3 -Release: 3 +Version: 0.9.4 +Release: 1 Summary: A firewall daemon with D-Bus interface providing a dynamic firewall License: GPLv2+ URL: http://www.firewalld.org @@ -9,14 +9,6 @@ Source0: https://github.com/firewalld/firewalld/archive/v%{version}.tar.gz#/%{ Patch0: firewalld-0.2.6-MDNS-default.patch Patch1: repair-test-cases.patch -Patch2: 0001-improvement-port-simplify-queryPort.patch -Patch3: 0001-improvement-port-allow-coalescing-and-breaking-of-ra.patch -Patch4: 0001-feat-implement-policy-objects-internally.patch -Patch5: 0001-fix-build-distribute-new-python-files.patch -Patch6: 0001-fix-po-add-new-python-files-to-POTFILES.patch -Patch7: 0001-fix-zone-listing-rich-rules-in-default-zone.patch -Patch8: 0001-fix-policy-ipXtables-calculate-max-name-len-properly.patch - BuildArch: noarch BuildRequires: autoconf automake desktop-file-utils gettext intltool glib2 glib2-devel systemd-units docbook-style-xsl BuildRequires: libxslt iptables ebtables ipset python3-devel @@ -202,6 +194,12 @@ fi %changelog +* Thu Jul 22 2021 gaihuiying - 0.9.4-1 +- Type:requirement +- ID:NA +- SUG:NA +- DESC:update to 0.9.4 + * Thu Dec 17 2020 Anakin Zhang - 0.8.3-3 - Type:requirement - ID:NA