// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2017 Intel Corporation */ #include "nm-default.h" #include "nm-iwd-manager.h" #include #include "nm-logging.h" #include "nm-core-internal.h" #include "nm-manager.h" #include "nm-device-iwd.h" #include "nm-wifi-utils.h" #include "nm-glib-aux/nm-random-utils.h" #include "settings/nm-settings.h" /*****************************************************************************/ typedef struct { const char *name; NMIwdNetworkSecurity security; char buf[0]; } KnownNetworkId; typedef struct { GDBusProxy *known_network; NMSettingsConnection *mirror_connection; } KnownNetworkData; typedef struct { NMManager *manager; NMSettings *settings; GCancellable *cancellable; gboolean running; GDBusObjectManager *object_manager; guint agent_id; char *agent_path; GHashTable *known_networks; } NMIwdManagerPrivate; struct _NMIwdManager { GObject parent; NMIwdManagerPrivate _priv; }; struct _NMIwdManagerClass { GObjectClass parent; }; G_DEFINE_TYPE (NMIwdManager, nm_iwd_manager, G_TYPE_OBJECT) #define NM_IWD_MANAGER_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMIwdManager, NM_IS_IWD_MANAGER) /*****************************************************************************/ #define _NMLOG_PREFIX_NAME "iwd-manager" #define _NMLOG_DOMAIN LOGD_WIFI #define _NMLOG(level, ...) \ G_STMT_START { \ if (nm_logging_enabled (level, _NMLOG_DOMAIN)) { \ char __prefix[32]; \ \ if (self) \ g_snprintf (__prefix, sizeof (__prefix), "%s[%p]", ""_NMLOG_PREFIX_NAME"", (self)); \ else \ g_strlcpy (__prefix, _NMLOG_PREFIX_NAME, sizeof (__prefix)); \ _nm_log ((level), (_NMLOG_DOMAIN), 0, NULL, NULL, \ "%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ __prefix _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ } \ } G_STMT_END /*****************************************************************************/ static void mirror_8021x_connection_take_and_delete (NMSettingsConnection *sett_conn); /*****************************************************************************/ static const char * get_variant_string_or_null (GVariant *v) { if (!v) return NULL; if ( !g_variant_is_of_type (v, G_VARIANT_TYPE_STRING) && !g_variant_is_of_type (v, G_VARIANT_TYPE_OBJECT_PATH)) return NULL; return g_variant_get_string (v, NULL); } static const char * get_property_string_or_null (GDBusProxy *proxy, const char *property) { gs_unref_variant GVariant *value = NULL; if (!proxy || !property) return NULL; value = g_dbus_proxy_get_cached_property (proxy, property); return get_variant_string_or_null (value); } static void agent_dbus_method_cb (GDBusConnection *connection, const char *sender, const char *object_path, const char *interface_name, const char *method_name, GVariant *parameters, GDBusMethodInvocation *invocation, gpointer user_data) { NMIwdManager *self = user_data; NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self); const char *network_path, *device_path, *ifname; gs_unref_object GDBusInterface *network = NULL, *device_obj = NULL; int ifindex; NMDevice *device; gs_free char *name_owner = NULL; int errsv; /* Be paranoid and check the sender address */ name_owner = g_dbus_object_manager_client_get_name_owner (G_DBUS_OBJECT_MANAGER_CLIENT (priv->object_manager)); if (!nm_streq0 (name_owner, sender)) goto return_error; if (!strcmp (method_name, "RequestUserPassword")) g_variant_get (parameters, "(&os)", &network_path, NULL); else g_variant_get (parameters, "(&o)", &network_path); network = g_dbus_object_manager_get_interface (priv->object_manager, network_path, NM_IWD_NETWORK_INTERFACE); if (!network) { _LOGE ("unable to find the network object"); return; } device_path = get_property_string_or_null (G_DBUS_PROXY (network), "Device"); if (!device_path) { _LOGD ("agent-request: device not cached for network %s in IWD Agent request", network_path); goto return_error; } device_obj = g_dbus_object_manager_get_interface (priv->object_manager, device_path, NM_IWD_DEVICE_INTERFACE); ifname = get_property_string_or_null (G_DBUS_PROXY (device_obj), "Name"); if (!ifname) { _LOGD ("agent-request: name not cached for device %s in IWD Agent request", device_path); goto return_error; } ifindex = if_nametoindex (ifname); if (!ifindex) { errsv = errno; _LOGD ("agent-request: if_nametoindex failed for Name %s for Device at %s: %i", ifname, device_path, errsv); goto return_error; } device = nm_manager_get_device_by_ifindex (priv->manager, ifindex); if (!NM_IS_DEVICE_IWD (device)) { _LOGD ("agent-request: IWD device named %s is not a Wifi device in IWD Agent request", ifname); goto return_error; } if (nm_device_iwd_agent_query (NM_DEVICE_IWD (device), invocation)) return; _LOGD ("agent-request: device %s did not handle the IWD Agent request", ifname); return_error: /* IWD doesn't look at the specific error */ g_dbus_method_invocation_return_error_literal (invocation, NM_DEVICE_ERROR, NM_DEVICE_ERROR_INVALID_CONNECTION, "Secrets not available for this connection"); } static const GDBusInterfaceInfo iwd_agent_iface_info = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT ( "net.connman.iwd.Agent", .methods = NM_DEFINE_GDBUS_METHOD_INFOS ( NM_DEFINE_GDBUS_METHOD_INFO ( "RequestPassphrase", .in_args = NM_DEFINE_GDBUS_ARG_INFOS ( NM_DEFINE_GDBUS_ARG_INFO ("network", "o"), ), .out_args = NM_DEFINE_GDBUS_ARG_INFOS ( NM_DEFINE_GDBUS_ARG_INFO ("passphrase", "s"), ), ), NM_DEFINE_GDBUS_METHOD_INFO ( "RequestPrivateKeyPassphrase", .in_args = NM_DEFINE_GDBUS_ARG_INFOS ( NM_DEFINE_GDBUS_ARG_INFO ("network", "o"), ), .out_args = NM_DEFINE_GDBUS_ARG_INFOS ( NM_DEFINE_GDBUS_ARG_INFO ("passphrase", "s"), ), ), NM_DEFINE_GDBUS_METHOD_INFO ( "RequestUserNameAndPassword", .in_args = NM_DEFINE_GDBUS_ARG_INFOS ( NM_DEFINE_GDBUS_ARG_INFO ("network", "o"), ), .out_args = NM_DEFINE_GDBUS_ARG_INFOS ( NM_DEFINE_GDBUS_ARG_INFO ("user", "s"), NM_DEFINE_GDBUS_ARG_INFO ("password", "s"), ), ), NM_DEFINE_GDBUS_METHOD_INFO ( "RequestUserPassword", .in_args = NM_DEFINE_GDBUS_ARG_INFOS ( NM_DEFINE_GDBUS_ARG_INFO ("network", "o"), NM_DEFINE_GDBUS_ARG_INFO ("user", "s"), ), .out_args = NM_DEFINE_GDBUS_ARG_INFOS ( NM_DEFINE_GDBUS_ARG_INFO ("password", "s"), ), ), ), ); static guint iwd_agent_export (GDBusConnection *connection, gpointer user_data, char **agent_path, GError **error) { static const GDBusInterfaceVTable vtable = { .method_call = agent_dbus_method_cb, }; char path[50]; unsigned int rnd; guint id; nm_utils_random_bytes (&rnd, sizeof (rnd)); nm_sprintf_buf (path, "/agent/%u", rnd); id = g_dbus_connection_register_object (connection, path, NM_UNCONST_PTR (GDBusInterfaceInfo, &iwd_agent_iface_info), &vtable, user_data, NULL, error); if (id) *agent_path = g_strdup (path); return id; } static void register_agent (NMIwdManager *self) { NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self); GDBusInterface *agent_manager; agent_manager = g_dbus_object_manager_get_interface (priv->object_manager, "/net/connman/iwd", NM_IWD_AGENT_MANAGER_INTERFACE); if (!agent_manager) { /* IWD prior to 1.0 dated 30 October, 2019 has the agent manager on a * different path. */ agent_manager = g_dbus_object_manager_get_interface (priv->object_manager, "/", NM_IWD_AGENT_MANAGER_INTERFACE); } if (!agent_manager) { _LOGE ("unable to register the IWD Agent: PSK/8021x Wi-Fi networks may not work"); return; } /* Register our agent */ g_dbus_proxy_call (G_DBUS_PROXY (agent_manager), "RegisterAgent", g_variant_new ("(o)", priv->agent_path), G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL); g_object_unref (agent_manager); } /*****************************************************************************/ static KnownNetworkId * known_network_id_new (const char *name, NMIwdNetworkSecurity security) { KnownNetworkId *id; gsize strsize = strlen (name) + 1; id = g_malloc (sizeof (KnownNetworkId) + strsize); id->name = id->buf; id->security = security; memcpy (id->buf, name, strsize); return id; } static guint known_network_id_hash (KnownNetworkId *id) { NMHashState h; nm_hash_init (&h, 1947951703u); nm_hash_update_val (&h, id->security); nm_hash_update_str (&h, id->name); return nm_hash_complete (&h); } static gboolean known_network_id_equal (KnownNetworkId *a, KnownNetworkId *b) { return a->security == b->security && nm_streq (a->name, b->name); } static void known_network_data_free (KnownNetworkData *network) { if (!network) return; g_object_unref (network->known_network); mirror_8021x_connection_take_and_delete (network->mirror_connection); g_slice_free (KnownNetworkData, network); } /*****************************************************************************/ static void set_device_dbus_object (NMIwdManager *self, GDBusProxy *proxy, GDBusObject *object) { NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self); const char *ifname; int ifindex; NMDevice *device; int errsv; ifname = get_property_string_or_null (proxy, "Name"); if (!ifname) { _LOGE ("Name not cached for Device at %s", g_dbus_proxy_get_object_path (proxy)); return; } ifindex = if_nametoindex (ifname); if (!ifindex) { errsv = errno; _LOGE ("if_nametoindex failed for Name %s for Device at %s: %i", ifname, g_dbus_proxy_get_object_path (proxy), errsv); return; } device = nm_manager_get_device_by_ifindex (priv->manager, ifindex); if (!NM_IS_DEVICE_IWD (device)) { _LOGE ("IWD device named %s is not a Wifi device", ifname); return; } nm_device_iwd_set_dbus_object (NM_DEVICE_IWD (device), object); } /* Look up an existing NMSettingsConnection for a WPA2-Enterprise network * that has been preprovisioned with an IWD config file, or create a new * in-memory connection object so that NM autoconnect mechanism and the * clients know this networks needs no additional EAP configuration from * the user. */ static NMSettingsConnection * mirror_8021x_connection (NMIwdManager *self, const char *name, gboolean create_new) { NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self); NMSettingsConnection *const*iter; gs_unref_object NMConnection *connection = NULL; NMSettingsConnection *settings_connection = NULL; char uuid[37]; NMSetting *setting; GError *error = NULL; gs_unref_bytes GBytes *new_ssid = NULL; for (iter = nm_settings_get_connections (priv->settings, NULL); *iter; iter++) { NMSettingsConnection *sett_conn = *iter; NMConnection *conn = nm_settings_connection_get_connection (sett_conn); NMIwdNetworkSecurity security; gs_free char *ssid_name = NULL; NMSettingWireless *s_wifi; NMSetting8021x *s_8021x; gboolean external = FALSE; guint i; security = nm_wifi_connection_get_iwd_security (conn, NULL); if (security != NM_IWD_NETWORK_SECURITY_8021X) continue; s_wifi = nm_connection_get_setting_wireless (conn); if (!s_wifi) continue; ssid_name = _nm_utils_ssid_to_utf8 (nm_setting_wireless_get_ssid (s_wifi)); if (!nm_streq (ssid_name, name)) continue; s_8021x = nm_connection_get_setting_802_1x (conn); for (i = 0; i < nm_setting_802_1x_get_num_eap_methods (s_8021x); i++) { if (nm_streq (nm_setting_802_1x_get_eap_method (s_8021x, i), "external")) { external = TRUE; break; } } /* Prefer returning connections for EAP method "external" */ if (!settings_connection || external) settings_connection = sett_conn; } /* If we already have an NMSettingsConnection matching this * KnownNetwork, whether it's saved or an in-memory connection * potentially created by ourselves then we have nothing left to * do here. */ if (settings_connection || !create_new) return settings_connection; connection = nm_simple_connection_new (); setting = NM_SETTING (g_object_new (NM_TYPE_SETTING_CONNECTION, NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRELESS_SETTING_NAME, NM_SETTING_CONNECTION_ID, name, NM_SETTING_CONNECTION_UUID, nm_utils_uuid_generate_buf (uuid), NULL)); nm_connection_add_setting (connection, setting); new_ssid = g_bytes_new (name, strlen (name)); setting = NM_SETTING (g_object_new (NM_TYPE_SETTING_WIRELESS, NM_SETTING_WIRELESS_SSID, new_ssid, NM_SETTING_WIRELESS_MODE, NM_SETTING_WIRELESS_MODE_INFRA, NULL)); nm_connection_add_setting (connection, setting); setting = NM_SETTING (g_object_new (NM_TYPE_SETTING_WIRELESS_SECURITY, NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open", NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-eap", NULL)); nm_connection_add_setting (connection, setting); /* "password" and "private-key-password" may be requested by the IWD agent * from NM and IWD will implement a specific secret cache policy so by * default respect that policy and don't save copies of those secrets in * NM settings. The saved values can not be used anyway because of our * use of NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW. */ setting = NM_SETTING (g_object_new (NM_TYPE_SETTING_802_1X, NM_SETTING_802_1X_PASSWORD_FLAGS, NM_SETTING_SECRET_FLAG_NOT_SAVED, NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD_FLAGS, NM_SETTING_SECRET_FLAG_NOT_SAVED, NULL)); nm_setting_802_1x_add_eap_method (NM_SETTING_802_1X (setting), "external"); nm_connection_add_setting (connection, setting); if (!nm_connection_normalize (connection, NULL, NULL, NULL)) return NULL; if (!nm_settings_add_connection (priv->settings, connection, NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY, NM_SETTINGS_CONNECTION_ADD_REASON_NONE, NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED, &settings_connection, &error)) { _LOGW ("failed to add a mirror NMConnection for IWD's Known Network '%s': %s", name, error->message); g_error_free (error); return NULL; } return settings_connection; } static void mirror_8021x_connection_take_and_delete (NMSettingsConnection *sett_conn) { NMSettingsConnectionIntFlags flags; if (!sett_conn) return; flags = nm_settings_connection_get_flags (sett_conn); /* If connection has not been saved since we created it * in interface_added it too can be removed now. */ if (NM_FLAGS_HAS (flags, NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED)) nm_settings_connection_delete (sett_conn, FALSE); g_object_unref (sett_conn); } static void interface_added (GDBusObjectManager *object_manager, GDBusObject *object, GDBusInterface *interface, gpointer user_data) { NMIwdManager *self = user_data; NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self); GDBusProxy *proxy; const char *iface_name; if (!priv->running) return; g_return_if_fail (G_IS_DBUS_PROXY (interface)); proxy = G_DBUS_PROXY (interface); iface_name = g_dbus_proxy_get_interface_name (proxy); if (nm_streq (iface_name, NM_IWD_DEVICE_INTERFACE)) { set_device_dbus_object (self, proxy, object); return; } if (nm_streq (iface_name, NM_IWD_KNOWN_NETWORK_INTERFACE)) { KnownNetworkId *id; KnownNetworkData *data; NMIwdNetworkSecurity security; const char *type_str, *name; NMSettingsConnection *sett_conn = NULL; type_str = get_property_string_or_null (proxy, "Type"); name = get_property_string_or_null (proxy, "Name"); if (!type_str || !name) return; if (nm_streq (type_str, "open")) security = NM_IWD_NETWORK_SECURITY_NONE; else if (nm_streq (type_str, "psk")) security = NM_IWD_NETWORK_SECURITY_PSK; else if (nm_streq (type_str, "8021x")) security = NM_IWD_NETWORK_SECURITY_8021X; else return; id = known_network_id_new (name, security); data = g_hash_table_lookup (priv->known_networks, id); if (data) { _LOGW ("DBus error: KnownNetwork already exists ('%s', %s)", name, type_str); g_free (id); nm_g_object_ref_set (&data->known_network, proxy); } else { data = g_slice_new0 (KnownNetworkData); data->known_network = g_object_ref (proxy); g_hash_table_insert (priv->known_networks, id, data); } if (security == NM_IWD_NETWORK_SECURITY_8021X) { sett_conn = mirror_8021x_connection (self, name, TRUE); if ( sett_conn && sett_conn != data->mirror_connection) { NMSettingsConnection *sett_conn_old = data->mirror_connection; data->mirror_connection = nm_g_object_ref (sett_conn); mirror_8021x_connection_take_and_delete (sett_conn_old); } } else mirror_8021x_connection_take_and_delete (g_steal_pointer (&data->mirror_connection)); return; } } static void interface_removed (GDBusObjectManager *object_manager, GDBusObject *object, GDBusInterface *interface, gpointer user_data) { NMIwdManager *self = user_data; NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self); GDBusProxy *proxy; const char *iface_name; g_return_if_fail (G_IS_DBUS_PROXY (interface)); proxy = G_DBUS_PROXY (interface); iface_name = g_dbus_proxy_get_interface_name (proxy); if (nm_streq (iface_name, NM_IWD_DEVICE_INTERFACE)) { set_device_dbus_object (self, proxy, NULL); return; } if (nm_streq (iface_name, NM_IWD_KNOWN_NETWORK_INTERFACE)) { KnownNetworkId id; const char *type_str; type_str = get_property_string_or_null (proxy, "Type"); id.name = get_property_string_or_null (proxy, "Name"); if (!type_str || !id.name) return; if (nm_streq (type_str, "open")) id.security = NM_IWD_NETWORK_SECURITY_NONE; else if (nm_streq (type_str, "psk")) id.security = NM_IWD_NETWORK_SECURITY_PSK; else if (nm_streq (type_str, "8021x")) id.security = NM_IWD_NETWORK_SECURITY_8021X; else return; g_hash_table_remove (priv->known_networks, &id); return; } } static void connection_removed (NMSettings *settings, NMSettingsConnection *sett_conn, gpointer user_data) { NMIwdManager *self = user_data; NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self); NMConnection *conn = nm_settings_connection_get_connection (sett_conn); NMSettingWireless *s_wireless; gboolean mapped; KnownNetworkData *data; KnownNetworkId id; id.security = nm_wifi_connection_get_iwd_security (conn, &mapped); if (!mapped) return; s_wireless = nm_connection_get_setting_wireless (conn); id.name = _nm_utils_ssid_to_utf8 (nm_setting_wireless_get_ssid (s_wireless)); data = g_hash_table_lookup (priv->known_networks, &id); g_free ((char *) id.name); if (!data) return; if (id.security == NM_IWD_NETWORK_SECURITY_8021X) { NMSettingsConnection *new_mirror_conn; if (data->mirror_connection != sett_conn) return; g_clear_object (&data->mirror_connection); /* Don't call Forget for an 8021x network until there's no * longer *any* matching NMSettingsConnection (debatable) */ new_mirror_conn = mirror_8021x_connection (self, id.name, FALSE); if (new_mirror_conn) { data->mirror_connection = g_object_ref (new_mirror_conn); return; } } if (!priv->running) return; g_dbus_proxy_call (data->known_network, "Forget", NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL); } static gboolean _om_has_name_owner (GDBusObjectManager *object_manager) { gs_free char *name_owner = NULL; nm_assert (G_IS_DBUS_OBJECT_MANAGER_CLIENT (object_manager)); name_owner = g_dbus_object_manager_client_get_name_owner (G_DBUS_OBJECT_MANAGER_CLIENT (object_manager)); return !!name_owner; } static void object_added (NMIwdManager *self, GDBusObject *object) { GList *interfaces, *iter; interfaces = g_dbus_object_get_interfaces (object); for (iter = interfaces; iter; iter = iter->next) { GDBusInterface *interface = G_DBUS_INTERFACE (iter->data); interface_added (NULL, object, interface, self); } g_list_free_full (interfaces, g_object_unref); } static void release_object_manager (NMIwdManager *self) { NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self); if (!priv->object_manager) return; g_signal_handlers_disconnect_by_data (priv->object_manager, self); if (priv->agent_id) { GDBusConnection *agent_connection; GDBusObjectManagerClient *omc = G_DBUS_OBJECT_MANAGER_CLIENT (priv->object_manager); agent_connection = g_dbus_object_manager_client_get_connection (omc); /* We're is called when we're shutting down (i.e. our DBus connection * is being closed, and IWD will detect this) or IWD was stopped so * in either case calling UnregisterAgent will not do anything. */ g_dbus_connection_unregister_object (agent_connection, priv->agent_id); priv->agent_id = 0; nm_clear_g_free (&priv->agent_path); } g_clear_object (&priv->object_manager); } static void prepare_object_manager (NMIwdManager *self); static void name_owner_changed (GObject *object, GParamSpec *pspec, gpointer user_data) { NMIwdManager *self = user_data; NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self); GDBusObjectManager *object_manager = G_DBUS_OBJECT_MANAGER (object); nm_assert (object_manager == priv->object_manager); if (_om_has_name_owner (object_manager)) { release_object_manager (self); prepare_object_manager (self); } else { const CList *tmp_lst; NMDevice *device; if (!priv->running) return; priv->running = false; nm_manager_for_each_device (priv->manager, device, tmp_lst) { if (NM_IS_DEVICE_IWD (device)) { nm_device_iwd_set_dbus_object (NM_DEVICE_IWD (device), NULL); } } } } static void device_added (NMManager *manager, NMDevice *device, gpointer user_data) { NMIwdManager *self = user_data; NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self); GList *objects, *iter; if (!NM_IS_DEVICE_IWD (device)) return; if (!priv->running) return; objects = g_dbus_object_manager_get_objects (priv->object_manager); for (iter = objects; iter; iter = iter->next) { GDBusObject *object = G_DBUS_OBJECT (iter->data); gs_unref_object GDBusInterface *interface = NULL; const char *obj_ifname; interface = g_dbus_object_get_interface (object, NM_IWD_DEVICE_INTERFACE); obj_ifname = get_property_string_or_null ((GDBusProxy *) interface, "Name"); if (!obj_ifname || strcmp (nm_device_get_iface (device), obj_ifname)) continue; nm_device_iwd_set_dbus_object (NM_DEVICE_IWD (device), object); break; } g_list_free_full (objects, g_object_unref); } static void got_object_manager (GObject *object, GAsyncResult *result, gpointer user_data) { NMIwdManager *self = user_data; NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self); GError *error = NULL; GDBusObjectManager *object_manager; GDBusConnection *connection; object_manager = g_dbus_object_manager_client_new_for_bus_finish (result, &error); if (object_manager == NULL) { _LOGE ("failed to acquire IWD Object Manager: Wi-Fi will not be available (%s)", error->message); g_clear_error (&error); return; } priv->object_manager = object_manager; g_signal_connect (priv->object_manager, "notify::name-owner", G_CALLBACK (name_owner_changed), self); nm_assert (G_IS_DBUS_OBJECT_MANAGER_CLIENT (object_manager)); connection = g_dbus_object_manager_client_get_connection (G_DBUS_OBJECT_MANAGER_CLIENT (object_manager)); priv->agent_id = iwd_agent_export (connection, self, &priv->agent_path, &error); if (!priv->agent_id) { _LOGE ("failed to export the IWD Agent: PSK/8021x Wi-Fi networks may not work: %s", error->message); g_clear_error (&error); } if (_om_has_name_owner (object_manager)) { GList *objects, *iter; priv->running = true; g_signal_connect (priv->object_manager, "interface-added", G_CALLBACK (interface_added), self); g_signal_connect (priv->object_manager, "interface-removed", G_CALLBACK (interface_removed), self); g_hash_table_remove_all (priv->known_networks); objects = g_dbus_object_manager_get_objects (object_manager); for (iter = objects; iter; iter = iter->next) object_added (self, G_DBUS_OBJECT (iter->data)); g_list_free_full (objects, g_object_unref); if (priv->agent_id) register_agent (self); } } static void prepare_object_manager (NMIwdManager *self) { NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self); g_dbus_object_manager_client_new_for_bus (NM_IWD_BUS_TYPE, G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE, NM_IWD_SERVICE, "/", NULL, NULL, NULL, priv->cancellable, got_object_manager, self); } gboolean nm_iwd_manager_is_known_network (NMIwdManager *self, const char *name, NMIwdNetworkSecurity security) { NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self); KnownNetworkId kn_id = { name, security }; return g_hash_table_contains (priv->known_networks, &kn_id); } GDBusProxy * nm_iwd_manager_get_dbus_interface (NMIwdManager *self, const char *path, const char *name) { NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self); GDBusInterface *interface; if (!priv->object_manager) return NULL; interface = g_dbus_object_manager_get_interface (priv->object_manager, path, name); return interface ? G_DBUS_PROXY (interface) : NULL; } /*****************************************************************************/ NM_DEFINE_SINGLETON_GETTER (NMIwdManager, nm_iwd_manager_get, NM_TYPE_IWD_MANAGER); static void nm_iwd_manager_init (NMIwdManager *self) { NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self); priv->manager = g_object_ref (NM_MANAGER_GET); g_signal_connect (priv->manager, NM_MANAGER_DEVICE_ADDED, G_CALLBACK (device_added), self); priv->settings = g_object_ref (NM_SETTINGS_GET); g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_REMOVED, G_CALLBACK (connection_removed), self); priv->cancellable = g_cancellable_new (); priv->known_networks = g_hash_table_new_full ((GHashFunc) known_network_id_hash, (GEqualFunc) known_network_id_equal, g_free, (GDestroyNotify) known_network_data_free); prepare_object_manager (self); } static void dispose (GObject *object) { NMIwdManager *self = (NMIwdManager *) object; NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self); release_object_manager (self); nm_clear_g_cancellable (&priv->cancellable); if (priv->settings) { g_signal_handlers_disconnect_by_data (priv->settings, self); g_clear_object (&priv->settings); } /* This may trigger mirror connection removals so it happens * after the g_signal_handlers_disconnect_by_data above. */ nm_clear_pointer (&priv->known_networks, g_hash_table_destroy); if (priv->manager) { g_signal_handlers_disconnect_by_data (priv->manager, self); g_clear_object (&priv->manager); } G_OBJECT_CLASS (nm_iwd_manager_parent_class)->dispose (object); } static void nm_iwd_manager_class_init (NMIwdManagerClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->dispose = dispose; }