|
|
8a3219 |
From 905f7eb62dd31a58b86fbfa191b2ce2482361b0b Mon Sep 17 00:00:00 2001
|
|
|
8a3219 |
From: Eric Garver <eric@garver.life>
|
|
|
8a3219 |
Date: Mon, 24 Jun 2019 10:36:40 -0400
|
|
|
8a3219 |
Subject: [PATCH 10/20] fix: dbus: fix service API break
|
|
|
8a3219 |
|
|
|
8a3219 |
This fixes a dbus API break that occurred when introducing service
|
|
|
8a3219 |
includes. The includes were added to the method's tuple, but doing so
|
|
|
8a3219 |
changed the dbus signature and thus broke the API. This restores the old
|
|
|
8a3219 |
signature.
|
|
|
8a3219 |
|
|
|
8a3219 |
Move to using key,value based import/export and sanity checking.
|
|
|
8a3219 |
Previously we were using a tuple with semi-undocumented positions.
|
|
|
8a3219 |
|
|
|
8a3219 |
Fixes: 1fc208bf9317 ("feat: service includes")
|
|
|
8a3219 |
Fixes: rhbz 1721414
|
|
|
8a3219 |
(cherry picked from commit 335a68c1bba5b1b1fbd430505a485a9eb035360c)
|
|
|
8a3219 |
---
|
|
|
8a3219 |
doc/xml/firewalld.dbus.xml | 5 ++-
|
|
|
8a3219 |
src/firewall/core/fw_config.py | 59 +++++++++++++++++++++++++++++++-
|
|
|
8a3219 |
src/firewall/core/io/service.py | 35 ++++++++++++++++---
|
|
|
8a3219 |
src/firewall/server/firewalld.py | 13 ++++++-
|
|
|
8a3219 |
4 files changed, 103 insertions(+), 9 deletions(-)
|
|
|
8a3219 |
|
|
|
8a3219 |
diff --git a/doc/xml/firewalld.dbus.xml b/doc/xml/firewalld.dbus.xml
|
|
|
8a3219 |
index 64d4d2b9c73b..cb4e1eac0fb9 100644
|
|
|
8a3219 |
--- a/doc/xml/firewalld.dbus.xml
|
|
|
8a3219 |
+++ b/doc/xml/firewalld.dbus.xml
|
|
|
8a3219 |
@@ -242,12 +242,12 @@
|
|
|
8a3219 |
</listitem>
|
|
|
8a3219 |
</varlistentry>
|
|
|
8a3219 |
<varlistentry id="FirewallD1.Methods.getServiceSettings">
|
|
|
8a3219 |
- <term><methodname>getServiceSettings</methodname>(s: <parameter>service</parameter>) → (sssa(ss)asa{ss}asa(ss)as)</term>
|
|
|
8a3219 |
+ <term><methodname>getServiceSettings</methodname>(s: <parameter>service</parameter>) → (sssa(ss)asa{ss}asa(ss))</term>
|
|
|
8a3219 |
<listitem>
|
|
|
8a3219 |
<para>
|
|
|
8a3219 |
Return runtime settings of given <replaceable>service</replaceable>.
|
|
|
8a3219 |
For getting permanent settings see <link linkend="FirewallD1.config.service.Methods.getSettings">org.fedoraproject.FirewallD1.config.service.Methods.getSettings</link>.
|
|
|
8a3219 |
- Settings are in format: <parameter>version</parameter>, <parameter>name</parameter>, <parameter>description</parameter>, array of <parameter>ports</parameter> (port, protocol), array of <parameter>module names</parameter>, dictionary of <parameter>destinations</parameter>, array of <parameter>protocols</parameter>, array of <parameter>source-ports</parameter> (port, protocol) and array of service <parameter>includes</parameter>.
|
|
|
8a3219 |
+ Settings are in format: <parameter>version</parameter>, <parameter>name</parameter>, <parameter>description</parameter>, array of <parameter>ports</parameter> (port, protocol), array of <parameter>module names</parameter>, dictionary of <parameter>destinations</parameter>, array of <parameter>protocols</parameter>, array of <parameter>source-ports</parameter> (port, protocol).
|
|
|
8a3219 |
</para>
|
|
|
8a3219 |
<para>
|
|
|
8a3219 |
<variablelist>
|
|
|
8a3219 |
@@ -259,7 +259,6 @@
|
|
|
8a3219 |
<varlistentry><term><parameter>destinations (a{ss})</parameter>: dictionary of {IP family : IP address} where 'IP family' key can be either 'ipv4' or 'ipv6'. See <literal>destination</literal> tag in <citerefentry><refentrytitle>firewalld.service</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</term></varlistentry>
|
|
|
8a3219 |
<varlistentry><term><parameter>protocols (as)</parameter>: array of protocols, see <literal>protocol</literal> tag in <citerefentry><refentrytitle>firewalld.service</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</term></varlistentry>
|
|
|
8a3219 |
<varlistentry><term><parameter>source-ports (a(ss))</parameter>: array of port and protocol pairs. See <literal>source-port</literal> tag in <citerefentry><refentrytitle>firewalld.service</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</term></varlistentry>
|
|
|
8a3219 |
- <varlistentry><term><parameter>includes (as)</parameter>: array of service includes, see <literal>include</literal> tag in <citerefentry><refentrytitle>firewalld.service</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</term></varlistentry>
|
|
|
8a3219 |
</variablelist>
|
|
|
8a3219 |
</para>
|
|
|
8a3219 |
<para>
|
|
|
8a3219 |
diff --git a/src/firewall/core/fw_config.py b/src/firewall/core/fw_config.py
|
|
|
8a3219 |
index a759cfdf83b3..8f29f0c416d2 100644
|
|
|
8a3219 |
--- a/src/firewall/core/fw_config.py
|
|
|
8a3219 |
+++ b/src/firewall/core/fw_config.py
|
|
|
8a3219 |
@@ -545,9 +545,43 @@ class FirewallConfig(object):
|
|
|
8a3219 |
return self._builtin_services[obj.name]
|
|
|
8a3219 |
|
|
|
8a3219 |
def get_service_config(self, obj):
|
|
|
8a3219 |
+ conf_dict = obj.export_config()
|
|
|
8a3219 |
+ conf_list = []
|
|
|
8a3219 |
+ for i in range(8): # tuple based dbus API has 8 elements
|
|
|
8a3219 |
+ if obj.IMPORT_EXPORT_STRUCTURE[i][0] not in conf_dict:
|
|
|
8a3219 |
+ # old API needs the empty elements as well. Grab it from the
|
|
|
8a3219 |
+ # object otherwise we don't know the type.
|
|
|
8a3219 |
+ conf_list.append(copy.deepcopy(getattr(obj, obj.IMPORT_EXPORT_STRUCTURE[i][0])))
|
|
|
8a3219 |
+ else:
|
|
|
8a3219 |
+ conf_list.append(conf_dict[obj.IMPORT_EXPORT_STRUCTURE[i][0]])
|
|
|
8a3219 |
+ return tuple(conf_list)
|
|
|
8a3219 |
+
|
|
|
8a3219 |
+ def get_service_config_dict(self, obj):
|
|
|
8a3219 |
return obj.export_config()
|
|
|
8a3219 |
|
|
|
8a3219 |
def set_service_config(self, obj, conf):
|
|
|
8a3219 |
+ conf_dict = {}
|
|
|
8a3219 |
+ for i,value in enumerate(conf):
|
|
|
8a3219 |
+ conf_dict[obj.IMPORT_EXPORT_STRUCTURE[i][0]] = value
|
|
|
8a3219 |
+
|
|
|
8a3219 |
+ if obj.builtin:
|
|
|
8a3219 |
+ x = copy.copy(obj)
|
|
|
8a3219 |
+ x.cleanup()
|
|
|
8a3219 |
+ x.import_config(conf_dict)
|
|
|
8a3219 |
+ x.path = config.ETC_FIREWALLD_SERVICES
|
|
|
8a3219 |
+ x.builtin = False
|
|
|
8a3219 |
+ if obj.path != x.path:
|
|
|
8a3219 |
+ x.default = False
|
|
|
8a3219 |
+ self.add_service(x)
|
|
|
8a3219 |
+ service_writer(x)
|
|
|
8a3219 |
+ return x
|
|
|
8a3219 |
+ else:
|
|
|
8a3219 |
+ obj.cleanup()
|
|
|
8a3219 |
+ obj.import_config(conf_dict)
|
|
|
8a3219 |
+ service_writer(obj)
|
|
|
8a3219 |
+ return obj
|
|
|
8a3219 |
+
|
|
|
8a3219 |
+ def set_service_config_dict(self, obj, conf):
|
|
|
8a3219 |
if obj.builtin:
|
|
|
8a3219 |
x = copy.copy(obj)
|
|
|
8a3219 |
x.import_config(conf)
|
|
|
8a3219 |
@@ -568,6 +602,29 @@ class FirewallConfig(object):
|
|
|
8a3219 |
raise FirewallError(errors.NAME_CONFLICT,
|
|
|
8a3219 |
"new_service(): '%s'" % name)
|
|
|
8a3219 |
|
|
|
8a3219 |
+ conf_dict = {}
|
|
|
8a3219 |
+ for i,value in enumerate(conf):
|
|
|
8a3219 |
+ conf_dict[Service.IMPORT_EXPORT_STRUCTURE[i][0]] = value
|
|
|
8a3219 |
+
|
|
|
8a3219 |
+ x = Service()
|
|
|
8a3219 |
+ x.check_name(name)
|
|
|
8a3219 |
+ x.import_config(conf_dict)
|
|
|
8a3219 |
+ x.name = name
|
|
|
8a3219 |
+ x.filename = "%s.xml" % name
|
|
|
8a3219 |
+ x.path = config.ETC_FIREWALLD_SERVICES
|
|
|
8a3219 |
+ # It is not possible to add a new one with a name of a buitin
|
|
|
8a3219 |
+ x.builtin = False
|
|
|
8a3219 |
+ x.default = True
|
|
|
8a3219 |
+
|
|
|
8a3219 |
+ service_writer(x)
|
|
|
8a3219 |
+ self.add_service(x)
|
|
|
8a3219 |
+ return x
|
|
|
8a3219 |
+
|
|
|
8a3219 |
+ def new_service_dict(self, name, conf):
|
|
|
8a3219 |
+ if name in self._services or name in self._builtin_services:
|
|
|
8a3219 |
+ raise FirewallError(errors.NAME_CONFLICT,
|
|
|
8a3219 |
+ "new_service(): '%s'" % name)
|
|
|
8a3219 |
+
|
|
|
8a3219 |
x = Service()
|
|
|
8a3219 |
x.check_name(name)
|
|
|
8a3219 |
x.import_config(conf)
|
|
|
8a3219 |
@@ -684,7 +741,7 @@ class FirewallConfig(object):
|
|
|
8a3219 |
return new_service
|
|
|
8a3219 |
|
|
|
8a3219 |
def _copy_service(self, obj, name):
|
|
|
8a3219 |
- return self.new_service(name, obj.export_config())
|
|
|
8a3219 |
+ return self.new_service_dict(name, obj.export_config())
|
|
|
8a3219 |
|
|
|
8a3219 |
# zones
|
|
|
8a3219 |
|
|
|
8a3219 |
diff --git a/src/firewall/core/io/service.py b/src/firewall/core/io/service.py
|
|
|
8a3219 |
index 3479dab7f175..44dc0ff8a9b0 100644
|
|
|
8a3219 |
--- a/src/firewall/core/io/service.py
|
|
|
8a3219 |
+++ b/src/firewall/core/io/service.py
|
|
|
8a3219 |
@@ -25,6 +25,8 @@ import xml.sax as sax
|
|
|
8a3219 |
import os
|
|
|
8a3219 |
import io
|
|
|
8a3219 |
import shutil
|
|
|
8a3219 |
+import copy
|
|
|
8a3219 |
+from collections import OrderedDict
|
|
|
8a3219 |
|
|
|
8a3219 |
from firewall import config
|
|
|
8a3219 |
from firewall.functions import u2b_if_py2
|
|
|
8a3219 |
@@ -47,7 +49,7 @@ class Service(IO_Object):
|
|
|
8a3219 |
( "source_ports", [ ( "", "" ), ], ), # a(ss)
|
|
|
8a3219 |
( "includes", [ "" ], ), # as
|
|
|
8a3219 |
)
|
|
|
8a3219 |
- DBUS_SIGNATURE = '(sssa(ss)asa{ss}asa(ss)as)'
|
|
|
8a3219 |
+ DBUS_SIGNATURE = '(sssa(ss)asa{ss}asa(ss))'
|
|
|
8a3219 |
ADDITIONAL_ALNUM_CHARS = [ "_", "-" ]
|
|
|
8a3219 |
PARSER_REQUIRED_ELEMENT_ATTRS = {
|
|
|
8a3219 |
"short": None,
|
|
|
8a3219 |
@@ -76,6 +78,34 @@ class Service(IO_Object):
|
|
|
8a3219 |
self.source_ports = [ ]
|
|
|
8a3219 |
self.includes = [ ]
|
|
|
8a3219 |
|
|
|
8a3219 |
+ def import_config(self, conf):
|
|
|
8a3219 |
+ self.check_config(conf)
|
|
|
8a3219 |
+
|
|
|
8a3219 |
+ for key in conf:
|
|
|
8a3219 |
+ if not hasattr(self, key):
|
|
|
8a3219 |
+ raise FirewallError(errors.UNKNOWN_ERROR, "Internal error. '{}' is not a valid attribute".format(key))
|
|
|
8a3219 |
+ if isinstance(conf[key], list):
|
|
|
8a3219 |
+ # maintain list order while removing duplicates
|
|
|
8a3219 |
+ setattr(self, key, list(OrderedDict.fromkeys(copy.deepcopy(conf[key]))))
|
|
|
8a3219 |
+ else:
|
|
|
8a3219 |
+ setattr(self, key, copy.deepcopy(conf[key]))
|
|
|
8a3219 |
+
|
|
|
8a3219 |
+ def export_config(self):
|
|
|
8a3219 |
+ conf = {}
|
|
|
8a3219 |
+ type_formats = dict([(x[0], x[1]) for x in self.IMPORT_EXPORT_STRUCTURE])
|
|
|
8a3219 |
+ for key in type_formats:
|
|
|
8a3219 |
+ if getattr(self, key):
|
|
|
8a3219 |
+ conf[key] = copy.deepcopy(getattr(self, key))
|
|
|
8a3219 |
+ return conf
|
|
|
8a3219 |
+
|
|
|
8a3219 |
+ def check_config(self, conf):
|
|
|
8a3219 |
+ type_formats = dict([(x[0], x[1]) for x in self.IMPORT_EXPORT_STRUCTURE])
|
|
|
8a3219 |
+ for key in conf:
|
|
|
8a3219 |
+ if key not in [x for (x,y) in self.IMPORT_EXPORT_STRUCTURE]:
|
|
|
8a3219 |
+ raise FirewallError(errors.INVALID_OPTION, "service option '{}' is not valid".format(key))
|
|
|
8a3219 |
+ self._check_config_structure(conf[key], type_formats[key])
|
|
|
8a3219 |
+ self._check_config(conf[key], key)
|
|
|
8a3219 |
+
|
|
|
8a3219 |
def cleanup(self):
|
|
|
8a3219 |
self.version = ""
|
|
|
8a3219 |
self.short = ""
|
|
|
8a3219 |
@@ -138,9 +168,6 @@ class Service(IO_Object):
|
|
|
8a3219 |
if len(module) < 2:
|
|
|
8a3219 |
raise FirewallError(errors.INVALID_MODULE, module)
|
|
|
8a3219 |
|
|
|
8a3219 |
- elif item == "includes":
|
|
|
8a3219 |
- pass
|
|
|
8a3219 |
-
|
|
|
8a3219 |
# PARSER
|
|
|
8a3219 |
|
|
|
8a3219 |
class service_ContentHandler(IO_Object_ContentHandler):
|
|
|
8a3219 |
diff --git a/src/firewall/server/firewalld.py b/src/firewall/server/firewalld.py
|
|
|
8a3219 |
index bc04f2d0f4c3..233160b64b18 100644
|
|
|
8a3219 |
--- a/src/firewall/server/firewalld.py
|
|
|
8a3219 |
+++ b/src/firewall/server/firewalld.py
|
|
|
8a3219 |
@@ -26,6 +26,7 @@ from gi.repository import GLib, GObject
|
|
|
8a3219 |
import sys
|
|
|
8a3219 |
sys.modules['gobject'] = GObject
|
|
|
8a3219 |
|
|
|
8a3219 |
+import copy
|
|
|
8a3219 |
import dbus
|
|
|
8a3219 |
import dbus.service
|
|
|
8a3219 |
import slip.dbus
|
|
|
8a3219 |
@@ -921,7 +922,17 @@ class FirewallD(slip.dbus.service.Object):
|
|
|
8a3219 |
# returns service settings for service
|
|
|
8a3219 |
service = dbus_to_python(service, str)
|
|
|
8a3219 |
log.debug1("getServiceSettings(%s)", service)
|
|
|
8a3219 |
- return self.fw.service.get_service(service).export_config()
|
|
|
8a3219 |
+ obj = self.fw.service.get_service(service)
|
|
|
8a3219 |
+ conf_dict = obj.export_config()
|
|
|
8a3219 |
+ conf_list = []
|
|
|
8a3219 |
+ for i in range(8): # tuple based dbus API has 8 elements
|
|
|
8a3219 |
+ if obj.IMPORT_EXPORT_STRUCTURE[i][0] not in conf_dict:
|
|
|
8a3219 |
+ # old API needs the empty elements as well. Grab it from the
|
|
|
8a3219 |
+ # object otherwise we don't know the type.
|
|
|
8a3219 |
+ conf_list.append(copy.deepcopy(getattr(obj, obj.IMPORT_EXPORT_STRUCTURE[i][0])))
|
|
|
8a3219 |
+ else:
|
|
|
8a3219 |
+ conf_list.append(conf_dict[obj.IMPORT_EXPORT_STRUCTURE[i][0]])
|
|
|
8a3219 |
+ return tuple(conf_list)
|
|
|
8a3219 |
|
|
|
8a3219 |
@slip.dbus.polkit.require_auth(config.dbus.PK_ACTION_INFO)
|
|
|
8a3219 |
@dbus_service_method(config.dbus.DBUS_INTERFACE, in_signature='',
|
|
|
8a3219 |
--
|
|
|
8a3219 |
2.20.1
|
|
|
8a3219 |
|