diff options
| author | Michael Biebl <biebl@debian.org> | 2021-02-11 18:11:46 +0100 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2021-02-11 18:11:46 +0100 |
| commit | 80ec1decc49c72efec2a8b87c06245c92c0ab807 (patch) | |
| tree | e3b229aa94e8dcf0590f2317664176e7b8f7607b /src/nm-firewall-manager.c | |
| parent | 65f86e8f56267192d42f2b629fc6b0c99fb9cd0c (diff) | |
New upstream version 1.29.90 upstream/1.29.90
Diffstat (limited to 'src/nm-firewall-manager.c')
| -rw-r--r-- | src/nm-firewall-manager.c | 623 |
1 files changed, 0 insertions, 623 deletions
diff --git a/src/nm-firewall-manager.c b/src/nm-firewall-manager.c deleted file mode 100644 index 7ae9ee89..00000000 --- a/src/nm-firewall-manager.c +++ /dev/null @@ -1,623 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (C) 2011 - 2015 Red Hat, Inc. - */ - -#include "nm-default.h" - -#include "nm-firewall-manager.h" - -#include "nm-glib-aux/nm-dbus-aux.h" -#include "c-list/src/c-list.h" - -#include "NetworkManagerUtils.h" -#include "nm-dbus-manager.h" - -#define FIREWALL_DBUS_SERVICE "org.fedoraproject.FirewallD1" -#define FIREWALL_DBUS_PATH "/org/fedoraproject/FirewallD1" -#define FIREWALL_DBUS_INTERFACE_ZONE "org.fedoraproject.FirewallD1.zone" - -/*****************************************************************************/ - -enum { STATE_CHANGED, LAST_SIGNAL }; - -static guint signals[LAST_SIGNAL] = {0}; - -typedef struct { - GDBusConnection *dbus_connection; - - GCancellable *get_name_owner_cancellable; - - CList pending_calls; - - guint name_owner_changed_id; - - bool dbus_inited : 1; - bool running : 1; -} NMFirewallManagerPrivate; - -struct _NMFirewallManager { - GObject parent; - NMFirewallManagerPrivate _priv; -}; - -struct _NMFirewallManagerClass { - GObjectClass parent; -}; - -G_DEFINE_TYPE(NMFirewallManager, nm_firewall_manager, G_TYPE_OBJECT) - -#define NM_FIREWALL_MANAGER_GET_PRIVATE(self) \ - _NM_GET_PRIVATE(self, NMFirewallManager, NM_IS_FIREWALL_MANAGER) - -/*****************************************************************************/ - -NM_DEFINE_SINGLETON_GETTER(NMFirewallManager, nm_firewall_manager_get, NM_TYPE_FIREWALL_MANAGER); - -/*****************************************************************************/ - -typedef enum { - OPS_TYPE_ADD = 1, - OPS_TYPE_CHANGE, - OPS_TYPE_REMOVE, -} OpsType; - -struct _NMFirewallManagerCallId { - CList lst; - - NMFirewallManager *self; - - char *iface; - - NMFirewallManagerAddRemoveCallback callback; - gpointer user_data; - - union { - struct { - GCancellable *cancellable; - GVariant * arg; - } dbus; - struct { - guint id; - } idle; - }; - - OpsType ops_type; - - bool is_idle : 1; -}; - -/*****************************************************************************/ - -static const char * -_ops_type_to_string(OpsType ops_type) -{ - switch (ops_type) { - case OPS_TYPE_ADD: - return "add"; - case OPS_TYPE_REMOVE: - return "remove"; - case OPS_TYPE_CHANGE: - return "change"; - } - nm_assert_not_reached(); - return NULL; -} - -#define _NMLOG_DOMAIN LOGD_FIREWALL -#define _NMLOG_PREFIX_NAME "firewall" -#define _NMLOG(level, call_id, ...) \ - G_STMT_START \ - { \ - if (nm_logging_enabled((level), (_NMLOG_DOMAIN))) { \ - NMFirewallManagerCallId *_call_id = (call_id); \ - char _prefix_name[30]; \ - char _prefix_info[100]; \ - \ - _nm_log((level), \ - (_NMLOG_DOMAIN), \ - 0, \ - NULL, \ - NULL, \ - "%s: %s" _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ - (self) != singleton_instance ? ({ \ - g_snprintf(_prefix_name, \ - sizeof(_prefix_name), \ - "%s[" NM_HASH_OBFUSCATE_PTR_FMT "]", \ - ""_NMLOG_PREFIX_NAME, \ - NM_HASH_OBFUSCATE_PTR(self)); \ - _prefix_name; \ - }) \ - : _NMLOG_PREFIX_NAME, \ - _call_id ? ({ \ - g_snprintf(_prefix_info, \ - sizeof(_prefix_info), \ - "[" NM_HASH_OBFUSCATE_PTR_FMT ",%s%s:%s%s%s]: ", \ - NM_HASH_OBFUSCATE_PTR(_call_id), \ - _ops_type_to_string(_call_id->ops_type), \ - _call_id->is_idle ? "*" : "", \ - NM_PRINT_FMT_QUOTE_STRING(_call_id->iface)); \ - _prefix_info; \ - }) \ - : "" _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ - } \ - } \ - G_STMT_END - -/*****************************************************************************/ - -static gboolean -_get_running(NMFirewallManagerPrivate *priv) -{ - /* when starting, we need to asynchronously check whether there is - * a name owner. During that time we optimistically assume that the - * service is indeed running. That is the time when we queue the - * requests, and they will be started once the get-name-owner call - * returns. */ - return priv->running || (priv->dbus_connection && !priv->dbus_inited); -} - -gboolean -nm_firewall_manager_get_running(NMFirewallManager *self) -{ - g_return_val_if_fail(NM_IS_FIREWALL_MANAGER(self), FALSE); - - return _get_running(NM_FIREWALL_MANAGER_GET_PRIVATE(self)); -} - -/*****************************************************************************/ - -static NMFirewallManagerCallId * -_cb_info_create(NMFirewallManager * self, - OpsType ops_type, - const char * iface, - const char * zone, - NMFirewallManagerAddRemoveCallback callback, - gpointer user_data) -{ - NMFirewallManagerPrivate *priv = NM_FIREWALL_MANAGER_GET_PRIVATE(self); - NMFirewallManagerCallId * call_id; - - call_id = g_slice_new0(NMFirewallManagerCallId); - - call_id->self = g_object_ref(self); - call_id->ops_type = ops_type; - call_id->iface = g_strdup(iface); - call_id->callback = callback; - call_id->user_data = user_data; - - if (_get_running(priv)) { - call_id->is_idle = FALSE; - call_id->dbus.arg = g_variant_new("(ss)", zone ?: "", iface); - } else - call_id->is_idle = TRUE; - - c_list_link_tail(&priv->pending_calls, &call_id->lst); - - return call_id; -} - -static void -_cb_info_complete(NMFirewallManagerCallId *call_id, GError *error) -{ - c_list_unlink(&call_id->lst); - - if (call_id->callback) - call_id->callback(call_id->self, call_id, error, call_id->user_data); - - if (call_id->is_idle) - nm_clear_g_source(&call_id->idle.id); - else { - nm_g_variant_unref(call_id->dbus.arg); - nm_clear_g_cancellable(&call_id->dbus.cancellable); - } - g_free(call_id->iface); - g_object_unref(call_id->self); - nm_g_slice_free(call_id); -} - -static gboolean -_handle_idle_cb(gpointer user_data) -{ - NMFirewallManager * self; - NMFirewallManagerCallId *call_id = user_data; - - nm_assert(call_id); - nm_assert(NM_IS_FIREWALL_MANAGER(call_id->self)); - nm_assert(call_id->is_idle); - nm_assert(c_list_contains(&NM_FIREWALL_MANAGER_GET_PRIVATE(call_id->self)->pending_calls, - &call_id->lst)); - - self = call_id->self; - - _LOGD(call_id, "complete: fake success"); - - call_id->idle.id = 0; - - _cb_info_complete(call_id, NULL); - return G_SOURCE_REMOVE; -} - -static gboolean -_handle_idle_start(NMFirewallManager *self, NMFirewallManagerCallId *call_id) -{ - if (!call_id->callback) { - /* if the user did not provide a callback and firewalld is not running, - * there is no point in scheduling an idle-request to fake success. Just - * return right away. */ - _LOGD(call_id, "complete: drop request simulating success"); - _cb_info_complete(call_id, NULL); - return FALSE; - } - call_id->idle.id = g_idle_add(_handle_idle_cb, call_id); - return TRUE; -} - -static void -_handle_dbus_cb(GObject *source, GAsyncResult *result, gpointer user_data) -{ - NMFirewallManager * self; - NMFirewallManagerCallId *call_id; - gs_free_error GError *error = NULL; - gs_unref_variant GVariant *ret = NULL; - - ret = g_dbus_connection_call_finish(G_DBUS_CONNECTION(source), result, &error); - - if (!ret && nm_utils_error_is_cancelled(error)) - return; - - call_id = user_data; - - nm_assert(call_id); - nm_assert(NM_IS_FIREWALL_MANAGER(call_id->self)); - nm_assert(!call_id->is_idle); - nm_assert(c_list_contains(&NM_FIREWALL_MANAGER_GET_PRIVATE(call_id->self)->pending_calls, - &call_id->lst)); - - self = call_id->self; - - if (error) { - const char *non_error = NULL; - - g_dbus_error_strip_remote_error(error); - - switch (call_id->ops_type) { - case OPS_TYPE_ADD: - case OPS_TYPE_CHANGE: - non_error = "ZONE_ALREADY_SET"; - break; - case OPS_TYPE_REMOVE: - non_error = "UNKNOWN_INTERFACE"; - break; - } - if (error->message && non_error && g_str_has_prefix(error->message, non_error) - && NM_IN_SET(error->message[strlen(non_error)], '\0', ':')) { - _LOGD(call_id, "complete: request failed with a non-error (%s)", error->message); - - /* The operation failed with an error reason that we don't want - * to propagate. Instead, signal success. */ - g_clear_error(&error); - } else - _LOGW(call_id, "complete: request failed (%s)", error->message); - } else - _LOGD(call_id, "complete: success"); - - g_clear_object(&call_id->dbus.cancellable); - - _cb_info_complete(call_id, error); -} - -static void -_handle_dbus_start(NMFirewallManager *self, NMFirewallManagerCallId *call_id) -{ - NMFirewallManagerPrivate *priv = NM_FIREWALL_MANAGER_GET_PRIVATE(self); - const char * dbus_method = NULL; - GVariant * arg; - - nm_assert(call_id); - nm_assert(priv->running); - nm_assert(!call_id->is_idle); - nm_assert(c_list_contains(&priv->pending_calls, &call_id->lst)); - - switch (call_id->ops_type) { - case OPS_TYPE_ADD: - dbus_method = "addInterface"; - break; - case OPS_TYPE_CHANGE: - dbus_method = "changeZone"; - break; - case OPS_TYPE_REMOVE: - dbus_method = "removeInterface"; - break; - } - nm_assert(dbus_method); - - arg = g_steal_pointer(&call_id->dbus.arg); - - nm_assert(arg && g_variant_is_floating(arg)); - - nm_assert(!call_id->dbus.cancellable); - - call_id->dbus.cancellable = g_cancellable_new(); - - g_dbus_connection_call(priv->dbus_connection, - FIREWALL_DBUS_SERVICE, - FIREWALL_DBUS_PATH, - FIREWALL_DBUS_INTERFACE_ZONE, - dbus_method, - arg, - NULL, - G_DBUS_CALL_FLAGS_NONE, - 10000, - call_id->dbus.cancellable, - _handle_dbus_cb, - call_id); -} - -static NMFirewallManagerCallId * -_start_request(NMFirewallManager * self, - OpsType ops_type, - const char * iface, - const char * zone, - NMFirewallManagerAddRemoveCallback callback, - gpointer user_data) -{ - NMFirewallManagerPrivate *priv; - NMFirewallManagerCallId * call_id; - - g_return_val_if_fail(NM_IS_FIREWALL_MANAGER(self), NULL); - g_return_val_if_fail(iface && *iface, NULL); - - priv = NM_FIREWALL_MANAGER_GET_PRIVATE(self); - - call_id = _cb_info_create(self, ops_type, iface, zone, callback, user_data); - - _LOGD(call_id, - "firewall zone %s %s:%s%s%s%s", - _ops_type_to_string(call_id->ops_type), - iface, - NM_PRINT_FMT_QUOTED(zone, "\"", zone, "\"", "default"), - call_id->is_idle ? " (not running, simulate success)" - : (!priv->running ? " (waiting to initialize)" : "")); - - if (!call_id->is_idle) { - if (priv->running) - _handle_dbus_start(self, call_id); - if (!call_id->callback) { - /* if the user did not provide a callback, the call_id is useless. - * Especially, the user cannot use the call-id to cancel the request, - * because he cannot know whether the request is still pending. - * - * Hence, returning %NULL doesn't mean that the request could not be started - * (this function never fails and always starts a request). */ - return NULL; - } - } else { - if (!_handle_idle_start(self, call_id)) { - /* if the user did not provide a callback and firewalld is not running, - * there is no point in scheduling an idle-request to fake success. Just - * return right away. */ - return NULL; - } - } - - return call_id; -} - -NMFirewallManagerCallId * -nm_firewall_manager_add_or_change_zone(NMFirewallManager *self, - const char * iface, - const char * zone, - gboolean add, /* TRUE == add, FALSE == change */ - NMFirewallManagerAddRemoveCallback callback, - gpointer user_data) -{ - return _start_request(self, - add ? OPS_TYPE_ADD : OPS_TYPE_CHANGE, - iface, - zone, - callback, - user_data); -} - -NMFirewallManagerCallId * -nm_firewall_manager_remove_from_zone(NMFirewallManager * self, - const char * iface, - const char * zone, - NMFirewallManagerAddRemoveCallback callback, - gpointer user_data) -{ - return _start_request(self, OPS_TYPE_REMOVE, iface, zone, callback, user_data); -} - -void -nm_firewall_manager_cancel_call(NMFirewallManagerCallId *call_id) -{ - NMFirewallManager * self; - NMFirewallManagerPrivate *priv; - gs_free_error GError *error = NULL; - - g_return_if_fail(call_id); - g_return_if_fail(NM_IS_FIREWALL_MANAGER(call_id->self)); - g_return_if_fail(!c_list_is_empty(&call_id->lst)); - - self = call_id->self; - priv = NM_FIREWALL_MANAGER_GET_PRIVATE(self); - - nm_assert(c_list_contains(&priv->pending_calls, &call_id->lst)); - - nm_utils_error_set_cancelled(&error, FALSE, "NMFirewallManager"); - - _LOGD(call_id, "complete: cancel (%s)", error->message); - - _cb_info_complete(call_id, error); -} - -/*****************************************************************************/ - -static void -name_owner_changed(NMFirewallManager *self, const char *owner) -{ - _nm_unused gs_unref_object NMFirewallManager *self_keep_alive = g_object_ref(self); - NMFirewallManagerPrivate * priv = NM_FIREWALL_MANAGER_GET_PRIVATE(self); - gboolean was_running; - gboolean now_running; - gboolean just_initied; - - owner = nm_str_not_empty(owner); - - if (!owner) - _LOGT(NULL, "D-Bus name for firewalld has no owner (firewall stopped)"); - else - _LOGT(NULL, "D-Bus name for firewalld has owner %s (firewall started)", owner); - - was_running = _get_running(priv); - just_initied = !priv->dbus_inited; - - priv->dbus_inited = TRUE; - priv->running = !!owner; - - now_running = _get_running(priv); - - if (just_initied) { - NMFirewallManagerCallId *call_id_safe; - NMFirewallManagerCallId *call_id; - - /* We kick of the requests that we have pending. Note that this is - * entirely asynchronous and also we don't invoke any callbacks for - * the user. - * Even _handle_idle_start() just schedules an idle handler. That is, - * because we don't want to callback to the user before emitting the - * DISCONNECTED signal below. Also, emitting callbacks means the user - * can call back to modify the list of pending-calls and we'd have - * to handle reentrancy. */ - c_list_for_each_entry_safe (call_id, call_id_safe, &priv->pending_calls, lst) { - nm_assert(!call_id->is_idle); - nm_assert(call_id->dbus.arg); - - if (priv->running) { - _LOGD(call_id, "initalizing: make D-Bus call"); - _handle_dbus_start(self, call_id); - } else { - /* we don't want to invoke callbacks to the user right away. That is because - * the user might schedule/cancel more calls, which messes up the order. - * - * Instead, convert the pending calls to idle requests... */ - nm_clear_pointer(&call_id->dbus.arg, g_variant_unref); - call_id->is_idle = TRUE; - _LOGD(call_id, "initializing: fake success on idle"); - _handle_idle_start(self, call_id); - } - } - } - - if (was_running != now_running) - g_signal_emit(self, signals[STATE_CHANGED], 0, FALSE); -} - -static void -name_owner_changed_cb(GDBusConnection *connection, - const char * sender_name, - const char * object_path, - const char * interface_name, - const char * signal_name, - GVariant * parameters, - gpointer user_data) -{ - NMFirewallManager *self = user_data; - const char * new_owner; - - if (!g_variant_is_of_type(parameters, G_VARIANT_TYPE("(sss)"))) - return; - - g_variant_get(parameters, "(&s&s&s)", NULL, NULL, &new_owner); - - name_owner_changed(self, new_owner); -} - -static void -get_name_owner_cb(const char *name_owner, GError *error, gpointer user_data) -{ - NMFirewallManager * self; - NMFirewallManagerPrivate *priv; - - if (!name_owner && g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) - return; - - self = user_data; - priv = NM_FIREWALL_MANAGER_GET_PRIVATE(self); - - g_clear_object(&priv->get_name_owner_cancellable); - - name_owner_changed(self, name_owner); -} - -/*****************************************************************************/ - -static void -nm_firewall_manager_init(NMFirewallManager *self) -{ - NMFirewallManagerPrivate *priv = NM_FIREWALL_MANAGER_GET_PRIVATE(self); - - c_list_init(&priv->pending_calls); - - priv->dbus_connection = nm_g_object_ref(NM_MAIN_DBUS_CONNECTION_GET); - - if (!priv->dbus_connection) { - _LOGD(NULL, "no D-Bus connection"); - return; - } - - priv->name_owner_changed_id = - nm_dbus_connection_signal_subscribe_name_owner_changed(priv->dbus_connection, - FIREWALL_DBUS_SERVICE, - name_owner_changed_cb, - self, - NULL); - - priv->get_name_owner_cancellable = g_cancellable_new(); - nm_dbus_connection_call_get_name_owner(priv->dbus_connection, - FIREWALL_DBUS_SERVICE, - -1, - priv->get_name_owner_cancellable, - get_name_owner_cb, - self); -} - -static void -dispose(GObject *object) -{ - NMFirewallManager * self = NM_FIREWALL_MANAGER(object); - NMFirewallManagerPrivate *priv = NM_FIREWALL_MANAGER_GET_PRIVATE(self); - - /* as every pending operation takes a reference to the manager, - * we don't expect pending operations at this point. */ - nm_assert(c_list_is_empty(&priv->pending_calls)); - - nm_clear_g_dbus_connection_signal(priv->dbus_connection, &priv->name_owner_changed_id); - - nm_clear_g_cancellable(&priv->get_name_owner_cancellable); - - G_OBJECT_CLASS(nm_firewall_manager_parent_class)->dispose(object); - - g_clear_object(&priv->dbus_connection); -} - -static void -nm_firewall_manager_class_init(NMFirewallManagerClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS(klass); - - object_class->dispose = dispose; - - signals[STATE_CHANGED] = g_signal_new(NM_FIREWALL_MANAGER_STATE_CHANGED, - G_OBJECT_CLASS_TYPE(object_class), - G_SIGNAL_RUN_FIRST, - 0, - NULL, - NULL, - g_cclosure_marshal_VOID__BOOLEAN, - G_TYPE_NONE, - 1, - G_TYPE_BOOLEAN /* initialized_now */); -} |