nm-iwd-manager.c 28.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
/* NetworkManager -- Network link manager
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Copyright (C) 2017 Intel Corporation
 */

#include "nm-default.h"

#include "nm-iwd-manager.h"

#include <net/if.h>

#include "nm-logging.h"
27
#include "nm-core-internal.h"
28 29
#include "nm-manager.h"
#include "nm-device-iwd.h"
30
#include "nm-wifi-utils.h"
31
#include "nm-glib-aux/nm-random-utils.h"
32
#include "settings/nm-settings.h"
33 34 35

/*****************************************************************************/

36
typedef struct {
37
	const char *name;
38
	NMIwdNetworkSecurity security;
39 40 41 42 43
	char buf[0];
} KnownNetworkId;

typedef struct {
	GDBusProxy *known_network;
44
	NMSettingsConnection *mirror_connection;
45 46
} KnownNetworkData;

47
typedef struct {
48
	NMManager *manager;
49
	NMSettings *settings;
50 51 52
	GCancellable *cancellable;
	gboolean running;
	GDBusObjectManager *object_manager;
53
	guint agent_id;
54
	char *agent_path;
55
	GHashTable *known_networks;
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
} 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

/*****************************************************************************/

93 94 95 96
static void mirror_8021x_connection_take_and_delete (NMSettingsConnection *sett_conn);

/*****************************************************************************/

97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
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);
}

123
static void
124
agent_dbus_method_cb (GDBusConnection *connection,
125 126
                      const char *sender, const char *object_path,
                      const char *interface_name, const char *method_name,
127 128 129
                      GVariant *parameters,
                      GDBusMethodInvocation *invocation,
                      gpointer user_data)
130 131 132
{
	NMIwdManager *self = user_data;
	NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self);
133
	const char *network_path, *device_path, *ifname;
134
	gs_unref_object GDBusInterface *network = NULL, *device_obj = NULL;
135
	int ifindex;
136
	NMDevice *device;
137
	gs_free char *name_owner = NULL;
138
	int errsv;
139 140

	/* Be paranoid and check the sender address */
141 142
	name_owner = g_dbus_object_manager_client_get_name_owner (G_DBUS_OBJECT_MANAGER_CLIENT (priv->object_manager));
	if (!nm_streq0 (name_owner, sender))
143 144
		goto return_error;

145 146 147 148
	if (!strcmp (method_name, "RequestUserPassword"))
		g_variant_get (parameters, "(&os)", &network_path, NULL);
	else
		g_variant_get (parameters, "(&o)", &network_path);
149 150 151 152 153

	network = g_dbus_object_manager_get_interface (priv->object_manager,
	                                               network_path,
	                                               NM_IWD_NETWORK_INTERFACE);

154
	device_path = get_property_string_or_null (G_DBUS_PROXY (network), "Device");
155
	if (!device_path) {
156
		_LOGD ("agent-request: device not cached for network %s in IWD Agent request",
157 158 159 160 161
		       network_path);
		goto return_error;
	}

	device_obj = g_dbus_object_manager_get_interface (priv->object_manager,
162 163
	                                                  device_path,
	                                                  NM_IWD_DEVICE_INTERFACE);
164

165
	ifname = get_property_string_or_null (G_DBUS_PROXY (device_obj), "Name");
166
	if (!ifname) {
167
		_LOGD ("agent-request: name not cached for device %s in IWD Agent request",
168 169 170 171 172 173
		       device_path);
		goto return_error;
	}

	ifindex = if_nametoindex (ifname);
	if (!ifindex) {
174
		errsv = errno;
175
		_LOGD ("agent-request: if_nametoindex failed for Name %s for Device at %s: %i",
176
		       ifname, device_path, errsv);
177 178 179
		goto return_error;
	}

180
	device = nm_manager_get_device_by_ifindex (priv->manager, ifindex);
181
	if (!NM_IS_DEVICE_IWD (device)) {
182 183
		_LOGD ("agent-request: IWD device named %s is not a Wifi device in IWD Agent request",
		       ifname);
184 185 186
		goto return_error;
	}

187
	if (nm_device_iwd_agent_query (NM_DEVICE_IWD (device), invocation))
188
		return;
189

190
	_LOGD ("agent-request: device %s did not handle the IWD Agent request", ifname);
191 192 193 194 195

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,
196
	                                               "Secrets not available for this connection");
197 198
}

199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242
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"),
			),
		),
	),
);

243
static guint
244
iwd_agent_export (GDBusConnection *connection, gpointer user_data,
245
                  char **agent_path, GError **error)
246
{
247 248
	static const GDBusInterfaceVTable vtable = {
		.method_call = agent_dbus_method_cb,
249
	};
250
	char path[50];
251 252 253
	unsigned int rnd;
	guint id;

254
	nm_utils_random_bytes (&rnd, sizeof (rnd));
255 256 257 258

	nm_sprintf_buf (path, "/agent/%u", rnd);

	id = g_dbus_connection_register_object (connection, path,
259
	                                        NM_UNCONST_PTR (GDBusInterfaceInfo, &iwd_agent_iface_info),
260
	                                        &vtable, user_data, NULL, error);
261 262 263 264 265 266

	if (id)
		*agent_path = g_strdup (path);
	return id;
}

267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286
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,
	                                                     "/",
	                                                     NM_IWD_AGENT_MANAGER_INTERFACE);

	/* 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);
}

287 288
/*****************************************************************************/

289 290 291 292
static KnownNetworkId *
known_network_id_new (const char *name, NMIwdNetworkSecurity security)
{
	KnownNetworkId *id;
293
	gsize strsize = strlen (name) + 1;
294 295 296 297 298 299 300 301 302

	id = g_malloc (sizeof (KnownNetworkId) + strsize);
	id->name = id->buf;
	id->security = security;
	memcpy (id->buf, name, strsize);

	return id;
}

303 304 305
static guint
known_network_id_hash (KnownNetworkId *id)
{
306 307 308 309 310 311
	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);
312 313 314 315 316
}

static gboolean
known_network_id_equal (KnownNetworkId *a, KnownNetworkId *b)
{
317 318
	return    a->security == b->security
	       && nm_streq (a->name, b->name);
319 320 321 322 323 324 325 326 327
}

static void
known_network_data_free (KnownNetworkData *network)
{
	if (!network)
		return;

	g_object_unref (network->known_network);
328
	mirror_8021x_connection_take_and_delete (network->mirror_connection);
329 330 331 332 333
	g_slice_free (KnownNetworkData, network);
}

/*****************************************************************************/

334
static void
335
set_device_dbus_object (NMIwdManager *self, GDBusProxy *proxy,
336 337 338 339
                        GDBusObject *object)
{
	NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self);
	const char *ifname;
340
	int ifindex;
341
	NMDevice *device;
342
	int errsv;
343

344 345
	ifname = get_property_string_or_null (proxy, "Name");
	if (!ifname) {
346 347 348 349 350 351 352 353
		_LOGE ("Name not cached for Device at %s",
		       g_dbus_proxy_get_object_path (proxy));
		return;
	}

	ifindex = if_nametoindex (ifname);

	if (!ifindex) {
354
		errsv = errno;
355
		_LOGE ("if_nametoindex failed for Name %s for Device at %s: %i",
356
		       ifname, g_dbus_proxy_get_object_path (proxy), errsv);
357 358 359
		return;
	}

360
	device = nm_manager_get_device_by_ifindex (priv->manager, ifindex);
361 362 363 364 365 366 367 368
	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);
}

369 370 371 372 373
/* 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.
374
 */
375
static NMSettingsConnection *
376
mirror_8021x_connection (NMIwdManager *self,
377 378
                         const char *name,
                         gboolean create_new)
379
{
380
	NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self);
381 382
	NMSettingsConnection *const*iter;
	gs_unref_object NMConnection *connection = NULL;
383
	NMSettingsConnection *settings_connection = NULL;
384 385 386
	char uuid[37];
	NMSetting *setting;
	GError *error = NULL;
387
	gs_unref_bytes GBytes *new_ssid = NULL;
388

389
	for (iter = nm_settings_get_connections (priv->settings, NULL); *iter; iter++) {
390 391
		NMSettingsConnection *sett_conn = *iter;
		NMConnection *conn = nm_settings_connection_get_connection (sett_conn);
392
		NMIwdNetworkSecurity security;
393
		gs_free char *ssid_name = NULL;
394
		NMSettingWireless *s_wifi;
395 396 397
		NMSetting8021x *s_8021x;
		gboolean external = FALSE;
		guint i;
398

399
		security = nm_wifi_connection_get_iwd_security (conn, NULL);
400 401 402
		if (security != NM_IWD_NETWORK_SECURITY_8021X)
			continue;

403
		s_wifi = nm_connection_get_setting_wireless (conn);
404 405 406
		if (!s_wifi)
			continue;

407
		ssid_name = _nm_utils_ssid_to_utf8 (nm_setting_wireless_get_ssid (s_wifi));
408

409 410 411 412 413 414 415 416 417 418 419 420 421 422
		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;
423 424
	}

425
	/* If we already have an NMSettingsConnection matching this
426
	 * KnownNetwork, whether it's saved or an in-memory connection
427 428
	 * potentially created by ourselves then we have nothing left to
	 * do here.
429
	 */
430
	if (settings_connection || !create_new)
431 432
		return settings_connection;

433 434 435 436
	connection = nm_simple_connection_new ();

	setting = NM_SETTING (g_object_new (NM_TYPE_SETTING_CONNECTION,
	                                    NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRELESS_SETTING_NAME,
437
	                                    NM_SETTING_CONNECTION_ID, name,
438 439 440 441 442
	                                    NM_SETTING_CONNECTION_UUID, nm_utils_uuid_generate_buf (uuid),
	                                    NM_SETTING_CONNECTION_READ_ONLY, TRUE,
	                                    NULL));
	nm_connection_add_setting (connection, setting);

443
	new_ssid = g_bytes_new (name, strlen (name));
444
	setting = NM_SETTING (g_object_new (NM_TYPE_SETTING_WIRELESS,
445
	                                    NM_SETTING_WIRELESS_SSID, new_ssid,
446 447 448 449 450 451 452 453 454 455
	                                    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);

456 457 458 459 460 461 462 463 464 465
	/* "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));
466 467 468
	nm_setting_802_1x_add_eap_method (NM_SETTING_802_1X (setting), "external");
	nm_connection_add_setting (connection, setting);

469 470 471
	if (!nm_connection_normalize (connection, NULL, NULL, NULL))
		return NULL;

472 473 474 475 476 477
	if (!nm_settings_add_connection (priv->settings,
	                                 connection,
	                                 NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY,
	                                 NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED,
	                                 &settings_connection,
	                                 &error)) {
478
		_LOGW ("failed to add a mirror NMConnection for IWD's Known Network '%s': %s",
479 480 481
		       name, error->message);
		g_error_free (error);
		return NULL;
482 483
	}

484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499
	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))
500
		nm_settings_connection_delete (sett_conn, FALSE);
501

502
	g_object_unref (sett_conn);
503 504
}

505 506 507 508 509
static void
interface_added (GDBusObjectManager *object_manager, GDBusObject *object,
                 GDBusInterface *interface, gpointer user_data)
{
	NMIwdManager *self = user_data;
510 511 512 513 514 515
	NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self);
	GDBusProxy *proxy;
	const char *iface_name;

	if (!priv->running)
		return;
516

517 518 519 520 521 522 523 524 525 526 527 528 529 530 531
	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;
532
		NMSettingsConnection *sett_conn = NULL;
533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549

		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);

550
		data = g_hash_table_lookup (priv->known_networks, id);
551 552 553
		if (data) {
			_LOGW ("DBus error: KnownNetwork already exists ('%s', %s)",
			       name, type_str);
554
			g_free (id);
555 556
			nm_g_object_ref_set (&data->known_network, proxy);
		} else {
557 558 559 560 561 562
			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) {
563
			sett_conn = mirror_8021x_connection (self, name, TRUE);
564

565 566 567 568 569 570 571 572 573
			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));
574

575 576
		return;
	}
577 578 579 580 581 582 583
}

static void
interface_removed (GDBusObjectManager *object_manager, GDBusObject *object,
                   GDBusInterface *interface, gpointer user_data)
{
	NMIwdManager *self = user_data;
584 585 586 587 588
	NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self);
	GDBusProxy *proxy;
	const char *iface_name;

	g_return_if_fail (G_IS_DBUS_PROXY (interface));
589

590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605
	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;
606

607 608 609 610 611 612 613 614 615 616 617 618
		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;
	}
619 620
}

621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669
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);
}

670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686
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);
687

688 689 690
	for (iter = interfaces; iter; iter = iter->next) {
		GDBusInterface *interface = G_DBUS_INTERFACE (iter->data);

691
		interface_added (NULL, object, interface, self);
692 693 694 695 696
	}

	g_list_free_full (interfaces, g_object_unref);
}

697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724
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);
}

725 726
static void prepare_object_manager (NMIwdManager *self);

727 728 729 730 731 732 733 734 735 736
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)) {
737
		release_object_manager (self);
738
		prepare_object_manager (self);
739
	} else {
740
		const CList *tmp_lst;
741
		NMDevice *device;
742

743 744 745
		if (!priv->running)
			return;

746 747
		priv->running = false;

748
		nm_manager_for_each_device (priv->manager, device, tmp_lst) {
749 750 751 752
			if (NM_IS_DEVICE_IWD (device)) {
				nm_device_iwd_set_dbus_object (NM_DEVICE_IWD (device),
				                               NULL);
			}
753 754 755 756 757
		}
	}
}

static void
758
device_added (NMManager *manager, NMDevice *device, gpointer user_data)
759 760 761 762 763 764 765 766 767 768 769 770 771 772
{
	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);
773
		gs_unref_object GDBusInterface *interface = NULL;
774 775 776 777
		const char *obj_ifname;

		interface = g_dbus_object_get_interface (object,
		                                         NM_IWD_DEVICE_INTERFACE);
778
		obj_ifname = get_property_string_or_null ((GDBusProxy *) interface, "Name");
779

780
		if (!obj_ifname || strcmp (nm_device_get_iface (device), obj_ifname))
781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801
			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)",
802
		       error->message);
803 804 805 806 807 808 809 810 811 812 813 814 815
		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));

816 817 818 819
	priv->agent_id = iwd_agent_export (connection,
	                                   self,
	                                   &priv->agent_path,
	                                   &error);
820
	if (!priv->agent_id) {
821
		_LOGE ("failed to export the IWD Agent: PSK/8021x Wi-Fi networks may not work: %s",
822
		       error->message);
823 824 825
		g_clear_error (&error);
	}

826 827 828 829 830 831 832 833 834 835
	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);

836 837
		g_hash_table_remove_all (priv->known_networks);

838 839 840 841 842 843 844 845 846
		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);
	}
847 848 849 850 851 852 853 854
}

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,
855
	                                          G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE,
856 857 858 859 860 861
	                                          NM_IWD_SERVICE, "/",
	                                          NULL, NULL, NULL,
	                                          priv->cancellable,
	                                          got_object_manager, self);
}

862
gboolean
863
nm_iwd_manager_is_known_network (NMIwdManager *self, const char *name,
864 865 866
                                 NMIwdNetworkSecurity security)
{
	NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self);
867
	KnownNetworkId kn_id = { name, security };
868

869
	return g_hash_table_contains (priv->known_networks, &kn_id);
870 871
}

872 873 874 875 876 877 878 879 880 881 882 883 884 885 886
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;
}

887 888 889 890 891 892 893 894 895 896
/*****************************************************************************/

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);

897 898
	priv->manager = g_object_ref (nm_manager_get ());
	g_signal_connect (priv->manager, NM_MANAGER_DEVICE_ADDED,
899 900
	                  G_CALLBACK (device_added), self);

901
	priv->settings = g_object_ref (NM_SETTINGS_GET);
902 903 904
	g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_REMOVED,
	                  G_CALLBACK (connection_removed), self);

905
	priv->cancellable = g_cancellable_new ();
906

907
	priv->known_networks = g_hash_table_new_full ((GHashFunc) known_network_id_hash,
908 909
	                                              (GEqualFunc) known_network_id_equal,
	                                              g_free,
910 911
	                                              (GDestroyNotify) known_network_data_free);

912 913 914 915 916 917 918 919 920
	prepare_object_manager (self);
}

static void
dispose (GObject *object)
{
	NMIwdManager *self = (NMIwdManager *) object;
	NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self);

921
	release_object_manager (self);
922 923 924

	nm_clear_g_cancellable (&priv->cancellable);

925 926 927 928 929 930 931 932
	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.
	 */
933
	nm_clear_pointer (&priv->known_networks, g_hash_table_destroy);
934

935 936 937
	if (priv->manager) {
		g_signal_handlers_disconnect_by_data (priv->manager, self);
		g_clear_object (&priv->manager);
938
	}
939

940 941 942 943 944 945 946 947 948 949
	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;
}