Adapted versions of commit d72384cfc3eba7db5739f83a28f3476b9553c856 Author: Jiri Popelka Date: Fri Jul 25 14:12:19 2014 +0200 Check built-in chains in direct chain handling functions. (RHBZ#1120619) also rename ipXtables/ebtables.CHAINS to BUILT_IN_CHAINS commit d4c839f838d0772b19d521ff826065e14f9a569d Author: Jiri Popelka Date: Mon Aug 4 15:59:54 2014 +0200 Direct & LockdownWhitelist: clear() -> cleanup() these were renamed in fb656f53bc commit fb656f53bc0eac095694ba61af6933632abf0f20 Author: Thomas Woerner Date: Tue Oct 22 17:21:55 2013 +0200 Fix cleanup and initializations to get leaked memory to 0 at all times commit 07550d550e618a3040153341eb8218551c3aa776 Author: Jiri Popelka Date: Tue Sep 30 16:39:41 2014 +0200 permanent direct: more tests for ipv & table see also 9139b468e5 commit 9139b468e5ffbe515dfd9892401eadb13a293a0b Author: Jiri Popelka Date: Tue Jul 15 18:21:47 2014 +0200 FirewallDirect: check ipv & table sooner to provide consistent errors thanks to Jakub Jelen commit 1a5670befb208018196b4f897fb84033e544f886 Author: Jiri Popelka Date: Tue Oct 14 09:46:46 2014 +0200 Rich_Rule.check(): action can't be used with icmp-block/forward-port/masquerade commit 76751826d97577fe2b41abf8c5448c653df49651 Author: Thomas Woerner Date: Tue Feb 11 23:34:09 2014 +0100 firewalld: No load failed error for absent direct.xml file commit 524438c41fae5a0b239d2273871ffe54c61e65de Author: Thomas Woerner Date: Tue Jul 7 13:01:12 2015 +0200 fw.py._start: Fix reload with runtime rules, but no direct.xml (RHBZ#1183008) diff -up firewalld-0.3.9/src/firewall/core/ebtables.py.RHBZ#1183008 firewalld-0.3.9/src/firewall/core/ebtables.py --- firewalld-0.3.9/src/firewall/core/ebtables.py.RHBZ#1183008 2015-07-07 13:10:10.938698154 +0200 +++ firewalld-0.3.9/src/firewall/core/ebtables.py 2015-07-07 13:10:11.074695768 +0200 @@ -25,7 +25,7 @@ from firewall.core.logger import log PROC_IPxTABLE_NAMES = { } -CHAINS = { +BUILT_IN_CHAINS = { "broute": [ "BROUTING" ], "nat": [ "PREROUTING", "POSTROUTING", "OUTPUT" ], "filter": [ "INPUT", "OUTPUT", "FORWARD" ], @@ -60,7 +60,7 @@ class ebtables: def available_tables(self, table=None): ret = [] - tables = [ table ] if table else CHAINS.keys() + tables = [ table ] if table else BUILT_IN_CHAINS.keys() for table in tables: try: self.__run(["-t", table, "-L"]) @@ -71,7 +71,7 @@ class ebtables: return ret def used_tables(self): - return list(CHAINS.keys()) + return list(BUILT_IN_CHAINS.keys()) def flush(self): tables = self.used_tables() @@ -86,13 +86,13 @@ class ebtables: if which == "used": tables = self.used_tables() else: - tables = list(CHAINS.keys()) + tables = list(BUILT_IN_CHAINS.keys()) if "nat" in tables: tables.remove("nat") # nat can not set policies in nat table for table in tables: - for chain in CHAINS[table]: + for chain in BUILT_IN_CHAINS[table]: self.__run([ "-t", table, "-P", chain, policy ]) ebtables_available_tables = ebtables().available_tables() diff -up firewalld-0.3.9/src/firewall/core/fw_config.py.RHBZ#1183008 firewalld-0.3.9/src/firewall/core/fw_config.py --- firewalld-0.3.9/src/firewall/core/fw_config.py.RHBZ#1183008 2013-12-03 14:59:48.000000000 +0100 +++ firewalld-0.3.9/src/firewall/core/fw_config.py 2015-07-07 13:13:50.709829789 +0200 @@ -131,7 +131,7 @@ class FirewallConfig: def update_direct(self): if not os.path.exists(FIREWALLD_DIRECT): - self._direct.clear() + self._direct.cleanup() else: self._direct.read() diff -up firewalld-0.3.9/src/firewall/core/fw_direct.py.RHBZ#1183008 firewalld-0.3.9/src/firewall/core/fw_direct.py --- firewalld-0.3.9/src/firewall/core/fw_direct.py.RHBZ#1183008 2015-07-07 13:10:11.072695804 +0200 +++ firewalld-0.3.9/src/firewall/core/fw_direct.py 2015-07-07 13:10:11.075695751 +0200 @@ -132,9 +132,41 @@ class FirewallDirect: except FirewallError as error: log.warning(str(error)) + def _check_ipv(self, ipv): + ipvs = ['ipv4', 'ipv6', 'eb'] + if ipv not in ipvs: + raise FirewallError(INVALID_IPV, + "'%s' not in '%s'" % (ipv, ipvs)) + + def _check_ipv_table(self, ipv, table): + self._check_ipv(ipv) + + tables = ipXtables.BUILT_IN_CHAINS.keys() if ipv in [ 'ipv4', 'ipv6' ] \ + else ebtables.BUILT_IN_CHAINS.keys() + if table not in tables: + raise FirewallError(INVALID_TABLE, + "'%s' not in '%s'" % (table, tables)) + + def _check_builtin_chain(self, ipv, table, chain): + if ipv in ['ipv4', 'ipv6']: + built_in_chains = ipXtables.BUILT_IN_CHAINS[table] + our_chains = ipXtables.OUR_CHAINS[table] + else: + built_in_chains = ebtables.BUILT_IN_CHAINS[table] + our_chains = ebtables.OUR_CHAINS[table] + if chain in built_in_chains: + raise FirewallError(BUILTIN_CHAIN, + "chain '%s' is built-in chain" % chain) + if chain in our_chains: + raise FirewallError(BUILTIN_CHAIN, + "chain '%s' is reserved" % chain) + + # DIRECT CHAIN def __chain(self, add, ipv, table, chain): + self._check_ipv_table(ipv, table) + self._check_builtin_chain(ipv, table, chain) table_id = (ipv, table) if add: @@ -174,11 +206,14 @@ class FirewallDirect: self.__chain(False, ipv, table, chain) def query_chain(self, ipv, table, chain): + self._check_ipv_table(ipv, table) + self._check_builtin_chain(ipv, table, chain) table_id = (ipv, table) - return (table_id in self._chains and \ - chain in self._chains[table_id]) + return (table_id in self._chains and + chain in self._chains[table_id]) def get_chains(self, ipv, table): + self._check_ipv_table(ipv, table) table_id = (ipv, table) if table_id in self._chains: return self._chains[table_id] @@ -195,13 +230,14 @@ class FirewallDirect: # DIRECT RULE def __rule(self, enable, ipv, table, chain, priority, args): + self._check_ipv_table(ipv, table) _chain = chain # use "%s_chain" for built-in chains if ipv in [ "ipv4", "ipv6" ]: - _CHAINS = ipXtables.CHAINS + _CHAINS = ipXtables.BUILT_IN_CHAINS else: - _CHAINS = ebtables.CHAINS + _CHAINS = ebtables.BUILT_IN_CHAINS if table in _CHAINS and chain in _CHAINS[table]: _chain = "%s_direct" % (chain) @@ -303,11 +339,13 @@ class FirewallDirect: self.__rule(False, ipv, table, chain, priority, args) def query_rule(self, ipv, table, chain, priority, args): + self._check_ipv_table(ipv, table) chain_id = (ipv, table, chain) return (chain_id in self._rules and \ (priority, args) in self._rules[chain_id]) def get_rules(self, ipv, table, chain): + self._check_ipv_table(ipv, table) chain_id = (ipv, table, chain) if chain_id in self._rules: return list(self._rules[chain_id].keys()) @@ -332,12 +370,6 @@ class FirewallDirect: # DIRECT PASSTHROUGH (tracked) - def _check_ipv(self, ipv): - ipvs = [ 'ipv4', 'ipv6', 'eb' ] - if ipv not in ipvs: - raise FirewallError(INVALID_IPV, - "'%s' not in '%s'" % (ipv, ipvs)) - def __passthrough(self, enable, ipv, args): self._check_ipv(ipv) diff -up firewalld-0.3.9/src/firewall/core/fw.py.RHBZ#1183008 firewalld-0.3.9/src/firewall/core/fw.py --- firewalld-0.3.9/src/firewall/core/fw.py.RHBZ#1183008 2015-07-07 13:10:11.072695804 +0200 +++ firewalld-0.3.9/src/firewall/core/fw.py 2015-07-07 13:10:11.075695751 +0200 @@ -203,15 +203,16 @@ class Firewall: self.zone.apply_zones() # load direct rules - log.debug1("Loading direct rules file '%s'" % FIREWALLD_DIRECT) obj = Direct(FIREWALLD_DIRECT) - try: - obj.read() - except Exception as msg: - log.debug1("Failed to load direct rules file '%s': %s", - FIREWALLD_DIRECT, msg) - else: - self.direct.set_permanent_config(obj) + if os.path.exists(FIREWALLD_DIRECT): + log.debug1("Loading direct rules file '%s'" % FIREWALLD_DIRECT) + try: + obj.read() + except Exception as msg: + log.debug1("Failed to load direct rules file '%s': %s", + FIREWALLD_DIRECT, msg) + + self.direct.set_permanent_config(obj) self.config.set_direct(copy.deepcopy(obj)) # check if default_zone is a valid zone @@ -394,7 +395,7 @@ class Firewall: rule.pop(1) table = None - for t in ipXtables.CHAINS.keys(): + for t in ipXtables.BUILT_IN_CHAINS.keys(): if t in rule: table = t if table and not self.is_table_available(ipv, table): diff -up firewalld-0.3.9/src/firewall/core/fw_zone.py.RHBZ#1183008 firewalld-0.3.9/src/firewall/core/fw_zone.py --- firewalld-0.3.9/src/firewall/core/fw_zone.py.RHBZ#1183008 2015-07-07 13:10:11.068695874 +0200 +++ firewalld-0.3.9/src/firewall/core/fw_zone.py 2015-07-07 13:10:11.076695733 +0200 @@ -26,7 +26,8 @@ from firewall.functions import portStr, checkProtocol, enable_ip_forwarding, check_single_address from firewall.core.rich import * from firewall.errors import * -from firewall.core.ipXtables import ip4tables_available_tables, ip6tables_available_tables +from firewall.core.ipXtables import ip4tables_available_tables,\ + ip6tables_available_tables, OUR_CHAINS mangle = [] if "mangle" in ip4tables_available_tables: @@ -187,6 +188,10 @@ class FirewallZone: ipvs.append("ipv6") for ipv in ipvs: + OUR_CHAINS[table].update(set([_zone, + "%s_log" % _zone, + "%s_deny" % _zone, + "%s_allow" % _zone])) chains.append((ipv, [ _zone, "-t", table ])) chains.append((ipv, [ "%s_log" % (_zone), "-t", table ])) chains.append((ipv, [ "%s_deny" % (_zone), "-t", table ])) diff -up firewalld-0.3.9/src/firewall/core/io/direct.py.RHBZ#1183008 firewalld-0.3.9/src/firewall/core/io/direct.py --- firewalld-0.3.9/src/firewall/core/io/direct.py.RHBZ#1183008 2015-07-07 13:10:11.030696540 +0200 +++ firewalld-0.3.9/src/firewall/core/io/direct.py 2015-07-07 13:10:11.076695733 +0200 @@ -29,6 +29,9 @@ from firewall.functions import splitArgs from firewall.errors import * from firewall.core.io.io_object import * from firewall.core.logger import log +from firewall.core import ipXtables +from firewall.core import ebtables + class direct_ContentHandler(IO_Object_ContentHandler): def __init__(self, item): @@ -188,9 +191,25 @@ class Direct(IO_Object): for args in self.passthroughs[key]: print (" ('%s')" % ("','".join(args))) + def _check_ipv(self, ipv): + ipvs = ['ipv4', 'ipv6', 'eb'] + if ipv not in ipvs: + raise FirewallError(INVALID_IPV, + "'%s' not in '%s'" % (ipv, ipvs)) + + def _check_ipv_table(self, ipv, table): + self._check_ipv(ipv) + + tables = ipXtables.BUILT_IN_CHAINS.keys() if ipv in ['ipv4', 'ipv6'] \ + else ebtables.BUILT_IN_CHAINS.keys() + if table not in tables: + raise FirewallError(INVALID_TABLE, + "'%s' not in '%s'" % (table, tables)) + # chains def add_chain(self, ipv, table, chain): + self._check_ipv_table(ipv, table) key = (ipv, table) if key not in self.chains: self.chains[key] = [ ] @@ -202,6 +221,7 @@ class Direct(IO_Object): + "already in list, ignoring") def remove_chain(self, ipv, table, chain): + self._check_ipv_table(ipv, table) key = (ipv, table) if key in self.chains and chain in self.chains[key]: self.chains[key].remove(chain) @@ -213,10 +233,12 @@ class Direct(IO_Object): (chain, table, ipv)) def query_chain(self, ipv, table, chain): + self._check_ipv_table(ipv, table) key = (ipv, table) return (key in self.chains and chain in self.chains[key]) def get_chains(self, ipv, table): + self._check_ipv_table(ipv, table) key = (ipv, table) if key in self.chains: return self.chains[key] @@ -230,6 +252,7 @@ class Direct(IO_Object): # rules def add_rule(self, ipv, table, chain, priority, args): + self._check_ipv_table(ipv, table) key = (ipv, table, chain) if key not in self.rules: self.rules[key] = LastUpdatedOrderedDict() @@ -243,6 +266,7 @@ class Direct(IO_Object): + "already in list, ignoring") def remove_rule(self, ipv, table, chain, priority, args): + self._check_ipv_table(ipv, table) key = (ipv, table, chain) value = (priority, tuple(args)) if key in self.rules and value in self.rules[key]: @@ -255,6 +279,7 @@ class Direct(IO_Object): "with ipv '%s' and priority %d not in list" % (ipv, priority)) def remove_rules(self, ipv, table, chain): + self._check_ipv_table(ipv, table) key = (ipv, table, chain) if key in self.rules: for value in self.rules[key].keys(): @@ -263,11 +288,13 @@ class Direct(IO_Object): del self.rules[key] def query_rule(self, ipv, table, chain, priority, args): + self._check_ipv_table(ipv, table) key = (ipv, table, chain) value = (priority, tuple(args)) return (key in self.rules and value in self.rules[key]) def get_rules(self, ipv, table, chain): + self._check_ipv_table(ipv, table) key = (ipv, table, chain) if key in self.rules: return self.rules[key] @@ -281,6 +308,7 @@ class Direct(IO_Object): # # passthrough # def add_passthrough(self, ipv, args): + self._check_ipv(ipv) if ipv not in self.passthroughs: self.passthroughs[ipv] = [ ] if args not in self.passthroughs[ipv]: @@ -291,6 +319,7 @@ class Direct(IO_Object): + "already in list, ignoring") def remove_passthrough(self, ipv, args): + self._check_ipv(ipv) if ipv in self.passthroughs and args in self.passthroughs[ipv]: self.passthroughs[ipv].remove(args) if len(self.passthroughs[ipv]) == 0: @@ -300,9 +329,11 @@ class Direct(IO_Object): ("',".join(args), ipv) + "not in list" def query_passthrough(self, ipv, args): + self._check_ipv(ipv) return (ipv in self.passthroughs and args in self.passthroughs[ipv]) def get_passthroughs(self, ipv): + self._check_ipv(ipv) if ipv in self.passthroughs: return self.passthroughs[ipv] else: diff -up firewalld-0.3.9/src/firewall/core/ipXtables.py.RHBZ#1183008 firewalld-0.3.9/src/firewall/core/ipXtables.py --- firewalld-0.3.9/src/firewall/core/ipXtables.py.RHBZ#1183008 2015-07-07 13:10:11.066695909 +0200 +++ firewalld-0.3.9/src/firewall/core/ipXtables.py 2015-07-07 13:10:11.076695733 +0200 @@ -34,7 +34,7 @@ PROC_IPxTABLE_NAMES = { "ipv6": "/proc/net/ip6_tables_names", } -CHAINS = { +BUILT_IN_CHAINS = { "security": [ "INPUT", "OUTPUT", "FORWARD" ], "raw": [ "PREROUTING", "OUTPUT" ], "mangle": [ "PREROUTING", "POSTROUTING", "INPUT", "OUTPUT", "FORWARD" ], @@ -53,38 +53,49 @@ ICMP = { } DEFAULT_RULES = { } +OUR_CHAINS = {} # chains created by firewalld DEFAULT_RULES["security"] = [ ] -for chain in CHAINS["security"]: +OUR_CHAINS["security"] = set() +for chain in BUILT_IN_CHAINS["security"]: DEFAULT_RULES["security"].append("-N %s_direct" % chain) DEFAULT_RULES["security"].append("-I %s 1 -j %s_direct" % (chain, chain)) + OUR_CHAINS["security"].add("%s_direct" % chain) DEFAULT_RULES["raw"] = [ ] -for chain in CHAINS["raw"]: +OUR_CHAINS["raw"] = set() +for chain in BUILT_IN_CHAINS["raw"]: DEFAULT_RULES["raw"].append("-N %s_direct" % chain) DEFAULT_RULES["raw"].append("-I %s 1 -j %s_direct" % (chain, chain)) + OUR_CHAINS["raw"].add("%s_direct" % chain) DEFAULT_RULES["mangle"] = [ ] -for chain in CHAINS["mangle"]: +OUR_CHAINS["mangle"] = set() +for chain in BUILT_IN_CHAINS["mangle"]: DEFAULT_RULES["mangle"].append("-N %s_direct" % chain) DEFAULT_RULES["mangle"].append("-I %s 1 -j %s_direct" % (chain, chain)) + OUR_CHAINS["mangle"].add("%s_direct" % chain) if chain == "PREROUTING": DEFAULT_RULES["mangle"].append("-N %s_ZONES_SOURCE" % chain) DEFAULT_RULES["mangle"].append("-N %s_ZONES" % chain) DEFAULT_RULES["mangle"].append("-I %s 2 -j %s_ZONES_SOURCE" % (chain, chain)) DEFAULT_RULES["mangle"].append("-I %s 3 -j %s_ZONES" % (chain, chain)) + OUR_CHAINS["mangle"].update(set(["%s_ZONES_SOURCE" % chain, "%s_ZONES" % chain])) DEFAULT_RULES["nat"] = [ ] -for chain in CHAINS["nat"]: +OUR_CHAINS["nat"] = set() +for chain in BUILT_IN_CHAINS["nat"]: DEFAULT_RULES["nat"].append("-N %s_direct" % chain) DEFAULT_RULES["nat"].append("-I %s 1 -j %s_direct" % (chain, chain)) + OUR_CHAINS["nat"].add("%s_direct" % chain) if chain in [ "PREROUTING", "POSTROUTING" ]: DEFAULT_RULES["nat"].append("-N %s_ZONES_SOURCE" % chain) DEFAULT_RULES["nat"].append("-N %s_ZONES" % chain) DEFAULT_RULES["nat"].append("-I %s 2 -j %s_ZONES_SOURCE" % (chain, chain)) DEFAULT_RULES["nat"].append("-I %s 3 -j %s_ZONES" % (chain, chain)) + OUR_CHAINS["nat"].update(set(["%s_ZONES_SOURCE" % chain, "%s_ZONES" % chain])) DEFAULT_RULES["filter"] = [ "-N INPUT_direct", @@ -119,6 +130,11 @@ DEFAULT_RULES["filter"] = [ "-I OUTPUT 1 -j OUTPUT_direct", ] +OUR_CHAINS["filter"] = set(["INPUT_direct", "INPUT_ZONES_SOURCE", "INPUT_ZONES", + "FORWARD_direct", "FORWARD_IN_ZONES_SOURCE", + "FORWARD_IN_ZONES", "FORWARD_OUT_ZONES_SOURCE", + "FORWARD_OUT_ZONES", "OUTPUT_direct"]) + class ip4tables: ipv = "ipv4" @@ -151,7 +167,7 @@ class ip4tables: def available_tables(self, table=None): ret = [] - tables = [ table ] if table else CHAINS.keys() + tables = [ table ] if table else BUILT_IN_CHAINS.keys() for table in tables: try: self.__run(["-t", table, "-L"]) @@ -199,13 +215,13 @@ class ip4tables: if which == "used": tables = self.used_tables() else: - tables = list(CHAINS.keys()) + tables = list(BUILT_IN_CHAINS.keys()) if "nat" in tables: tables.remove("nat") # nat can not set policies in nat table for table in tables: - for chain in CHAINS[table]: + for chain in BUILT_IN_CHAINS[table]: self.__run([ "-t", table, "-P", chain, policy ]) class ip6tables(ip4tables): diff -up firewalld-0.3.9/src/firewall/core/rich.py.RHBZ#1183008 firewalld-0.3.9/src/firewall/core/rich.py --- firewalld-0.3.9/src/firewall/core/rich.py.RHBZ#1183008 2015-07-07 13:10:11.070695839 +0200 +++ firewalld-0.3.9/src/firewall/core/rich.py 2015-07-07 13:10:11.076695733 +0200 @@ -481,6 +481,8 @@ class Rich_Rule(object): elif type(self.element) == Rich_Masquerade: if self.destination != None: raise FirewallError(INVALID_RULE, "masquerade and destination") + if self.action: + raise FirewallError(INVALID_RULE, "masquerade and action") # icmp-block elif type(self.element) == Rich_IcmpBlock: @@ -488,8 +490,8 @@ class Rich_Rule(object): # knowledge about this, therefore only simple check if self.element.name == None or len(self.element.name) < 1: raise FirewallError(INVALID_ICMPTYPE, str(self.element.name)) - if self.action and type(self.action) == Rich_Accept: - raise FirewallError(INVALID_RULE, "icmpblock and accept") + if self.action: + raise FirewallError(INVALID_RULE, "icmp-block and action") # forward-port elif type(self.element) == Rich_ForwardPort: @@ -508,6 +510,8 @@ class Rich_Rule(object): raise FirewallError(INVALID_ADDR, self.element.to_address) if self.family == None: raise FirewallError(INVALID_FAMILY) + if self.action: + raise FirewallError(INVALID_RULE, "forward-port and action") # other element and not empty? elif self.element != None: