nm-settings.c 71.3 KB
Newer Older
1
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2 3 4
/* NetworkManager system settings service
 *
 * Søren Sandmann <sandmann@daimi.au.dk>
5 6
 * Dan Williams <dcbw@redhat.com>
 * Tambet Ingo <tambet@gmail.com>
7 8 9 10 11 12 13 14 15 16 17
 *
 * 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.
 *
18 19 20
 * 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.
21
 *
22
 * (C) Copyright 2007 - 2011 Red Hat, Inc.
23
 * (C) Copyright 2008 Novell, Inc.
24 25
 */

26
#include "nm-default.h"
27

28 29
#include "nm-settings.h"

30
#include <unistd.h>
31
#include <sys/stat.h>
32
#include <gmodule.h>
33
#include <pwd.h>
34

35 36 37 38
#if HAVE_SELINUX
#include <selinux/selinux.h>
#endif

39
#include "nm-libnm-core-intern/nm-common-macros.h"
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
#include "nm-dbus-interface.h"
#include "nm-connection.h"
#include "nm-setting-8021x.h"
#include "nm-setting-bluetooth.h"
#include "nm-setting-cdma.h"
#include "nm-setting-connection.h"
#include "nm-setting-gsm.h"
#include "nm-setting-ip4-config.h"
#include "nm-setting-ip6-config.h"
#include "nm-setting-olpc-mesh.h"
#include "nm-setting-ppp.h"
#include "nm-setting-pppoe.h"
#include "nm-setting-serial.h"
#include "nm-setting-vpn.h"
#include "nm-setting-wired.h"
#include "nm-setting-adsl.h"
#include "nm-setting-wireless.h"
#include "nm-setting-wireless-security.h"
58
#include "nm-setting-proxy.h"
59 60
#include "nm-setting-bond.h"
#include "nm-utils.h"
61
#include "nm-core-internal.h"
62

63
#include "nm-glib-aux/nm-c-list.h"
64
#include "nm-dbus-object.h"
65
#include "devices/nm-device-ethernet.h"
66
#include "nm-settings-connection.h"
67
#include "nm-settings-plugin.h"
68
#include "nm-dbus-manager.h"
69
#include "nm-auth-utils.h"
70
#include "nm-auth-subject.h"
71
#include "nm-session-monitor.h"
Thomas Haller's avatar
Thomas Haller committed
72
#include "plugins/keyfile/nms-keyfile-plugin.h"
73
#include "nm-agent-manager.h"
74
#include "nm-config.h"
75
#include "nm-audit-manager.h"
76
#include "NetworkManagerUtils.h"
77
#include "nm-dispatcher.h"
78
#include "nm-hostname-manager.h"
79

80
/*****************************************************************************/
81

82 83
#define EXPORT(sym) void * __export_##sym = &sym;

84
EXPORT(nm_settings_connection_get_type)
85
EXPORT(nm_settings_connection_update)
86 87

/*****************************************************************************/
88

89 90 91 92
static NM_CACHED_QUARK_FCN ("plugin-module-path", plugin_module_path_quark)
static NM_CACHED_QUARK_FCN ("default-wired-connection", _default_wired_connection_quark)
static NM_CACHED_QUARK_FCN ("default-wired-device", _default_wired_device_quark)

93
/*****************************************************************************/
94

95 96 97 98 99 100 101
NM_GOBJECT_PROPERTIES_DEFINE (NMSettings,
	PROP_UNMANAGED_SPECS,
	PROP_HOSTNAME,
	PROP_CAN_MODIFY,
	PROP_CONNECTIONS,
	PROP_STARTUP_COMPLETE,
);
102

103 104 105 106
enum {
	CONNECTION_ADDED,
	CONNECTION_UPDATED,
	CONNECTION_REMOVED,
107
	CONNECTION_FLAGS_CHANGED,
108 109
	LAST_SIGNAL
};
110

111
static guint signals[LAST_SIGNAL] = { 0 };
112

113
typedef struct {
114 115
	NMAgentManager *agent_mgr;

116
	NMConfig *config;
117

118
	GSList *auths;
119

120
	GSList *plugins;
121 122 123

	CList connections_lst_head;

124
	NMSettingsConnection **connections_cached_list;
125
	GSList *unmanaged_specs;
126
	GSList *unrecognized_specs;
127

128 129
	NMHostnameManager *hostname_manager;

130 131
	NMSettingsConnection *startup_complete_blocked_by;

132 133 134 135 136 137
	guint connections_len;

	bool started:1;
	bool startup_complete:1;
	bool connections_loaded:1;

138
} NMSettingsPrivate;
139

140
struct _NMSettings {
141
	NMDBusObject parent;
142 143
	NMSettingsPrivate _priv;
};
144

145
struct _NMSettingsClass {
146
	NMDBusObjectClass parent;
147 148
};

149
G_DEFINE_TYPE (NMSettings, nm_settings, NM_TYPE_DBUS_OBJECT);
150 151 152 153 154 155

#define NM_SETTINGS_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMSettings, NM_IS_SETTINGS)

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

#define _NMLOG_DOMAIN         LOGD_SETTINGS
156
#define _NMLOG(level, ...) __NMLOG_DEFAULT (level, _NMLOG_DOMAIN, "settings", __VA_ARGS__)
157 158 159

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

160 161 162 163
static const NMDBusInterfaceInfoExtended interface_info_settings;
static const GDBusSignalInfo signal_info_new_connection;
static const GDBusSignalInfo signal_info_connection_removed;

164 165 166 167 168 169 170 171 172 173
static void claim_connection (NMSettings *self,
                              NMSettingsConnection *connection);

static void unmanaged_specs_changed (NMSettingsPlugin *config, gpointer user_data);
static void unrecognized_specs_changed (NMSettingsPlugin *config, gpointer user_data);

static void connection_ready_changed (NMSettingsConnection *conn,
                                      GParamSpec *pspec,
                                      gpointer user_data);

174 175 176 177 178
static void default_wired_clear_tag (NMSettings *self,
                                     NMDevice *device,
                                     NMSettingsConnection *connection,
                                     gboolean add_to_no_auto_default);

179
/*****************************************************************************/
180

181 182 183 184
static void
check_startup_complete (NMSettings *self)
{
	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
185
	NMSettingsConnection *sett_conn;
186 187 188 189

	if (priv->startup_complete)
		return;

190 191 192
	c_list_for_each_entry (sett_conn, &priv->connections_lst_head, _connections_lst) {
		if (!nm_settings_connection_get_ready (sett_conn)) {
			nm_g_object_ref_set (&priv->startup_complete_blocked_by, sett_conn);
193
			return;
194
		}
195 196
	}

197 198
	g_clear_object (&priv->startup_complete_blocked_by);

199
	/* the connection_ready_changed signal handler is no longer needed. */
200 201
	c_list_for_each_entry (sett_conn, &priv->connections_lst_head, _connections_lst)
		g_signal_handlers_disconnect_by_func (sett_conn, G_CALLBACK (connection_ready_changed), self);
202

203
	priv->startup_complete = TRUE;
204
	_notify (self, PROP_STARTUP_COMPLETE);
205 206 207 208 209 210 211 212 213 214 215 216 217
}

static void
connection_ready_changed (NMSettingsConnection *conn,
                          GParamSpec *pspec,
                          gpointer user_data)
{
	NMSettings *self = NM_SETTINGS (user_data);

	if (nm_settings_connection_get_ready (conn))
		check_startup_complete (self);
}

218
static void
219
plugin_connection_added (NMSettingsPlugin *config,
220
                         NMSettingsConnection *connection,
221
                         NMSettings *self)
222
{
223
	claim_connection (self, connection);
224 225
}

226
static void
227
load_connections (NMSettings *self)
228
{
229
	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
230 231 232
	GSList *iter;

	for (iter = priv->plugins; iter; iter = g_slist_next (iter)) {
233
		NMSettingsPlugin *plugin = NM_SETTINGS_PLUGIN (iter->data);
234 235 236
		GSList *plugin_connections;
		GSList *elt;

237
		plugin_connections = nm_settings_plugin_get_connections (plugin);
238 239 240 241 242 243

		// FIXME: ensure connections from plugins loaded with a lower priority
		// get rejected when they conflict with connections from a higher
		// priority plugin.

		for (elt = plugin_connections; elt; elt = g_slist_next (elt))
244
			claim_connection (self, elt->data);
245 246

		g_slist_free (plugin_connections);
247

248
		g_signal_connect (plugin, NM_SETTINGS_PLUGIN_CONNECTION_ADDED,
249
		                  G_CALLBACK (plugin_connection_added), self);
250
		g_signal_connect (plugin, NM_SETTINGS_PLUGIN_UNMANAGED_SPECS_CHANGED,
251
		                  G_CALLBACK (unmanaged_specs_changed), self);
252
		g_signal_connect (plugin, NM_SETTINGS_PLUGIN_UNRECOGNIZED_SPECS_CHANGED,
253
		                  G_CALLBACK (unrecognized_specs_changed), self);
254 255
	}

256
	priv->connections_loaded = TRUE;
257
	_notify (self, PROP_CONNECTIONS);
258

259
	unmanaged_specs_changed (NULL, self);
260
	unrecognized_specs_changed (NULL, self);
261 262
}

Dan Winship's avatar
Dan Winship committed
263
static void
264 265 266
impl_settings_list_connections (NMDBusObject *obj,
                                const NMDBusInterfaceInfoExtended *interface_info,
                                const NMDBusMethodInfoExtended *method_info,
267
                                GDBusConnection *dbus_connection,
268 269 270
                                const char *sender,
                                GDBusMethodInvocation *invocation,
                                GVariant *parameters)
271
{
272
	NMSettings *self = NM_SETTINGS (obj);
273
	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
274
	gs_free const char **strv = NULL;
Dan Winship's avatar
Dan Winship committed
275

276 277 278 279
	strv = nm_dbus_utils_get_paths_for_clist (&priv->connections_lst_head,
	                                          priv->connections_len,
	                                          G_STRUCT_OFFSET (NMSettingsConnection, _connections_lst),
	                                          TRUE);
280
	g_dbus_method_invocation_return_value (invocation,
281
	                                       g_variant_new ("(^ao)", strv));
282 283
}

284 285
NMSettingsConnection *
nm_settings_get_connection_by_uuid (NMSettings *self, const char *uuid)
286
{
287 288 289 290 291 292 293
	NMSettingsPrivate *priv;
	NMSettingsConnection *candidate;

	g_return_val_if_fail (NM_IS_SETTINGS (self), NULL);
	g_return_val_if_fail (uuid != NULL, NULL);

	priv = NM_SETTINGS_GET_PRIVATE (self);
294

295 296
	c_list_for_each_entry (candidate, &priv->connections_lst_head, _connections_lst) {
		if (nm_streq (uuid, nm_settings_connection_get_uuid (candidate)))
297
			return candidate;
298 299
	}

300 301 302
	return NULL;
}

303
static void
304 305 306 307 308 309 310
impl_settings_get_connection_by_uuid (NMDBusObject *obj,
                                      const NMDBusInterfaceInfoExtended *interface_info,
                                      const NMDBusMethodInfoExtended *method_info,
                                      GDBusConnection *dbus_connection,
                                      const char *sender,
                                      GDBusMethodInvocation *invocation,
                                      GVariant *parameters)
311
{
312
	NMSettings *self = NM_SETTINGS (obj);
313
	NMSettingsConnection *sett_conn;
314
	gs_unref_object NMAuthSubject *subject = NULL;
315
	GError *error = NULL;
316 317 318
	const char *uuid;

	g_variant_get (parameters, "(&s)", &uuid);
319

320 321
	sett_conn = nm_settings_get_connection_by_uuid (self, uuid);
	if (!sett_conn) {
322 323 324 325 326 327
		error = g_error_new_literal (NM_SETTINGS_ERROR,
		                             NM_SETTINGS_ERROR_INVALID_CONNECTION,
		                             "No connection with the UUID was found.");
		goto error;
	}

328
	subject = nm_auth_subject_new_unix_process_from_context (invocation);
329 330 331 332 333 334 335
	if (!subject) {
		error = g_error_new_literal (NM_SETTINGS_ERROR,
		                             NM_SETTINGS_ERROR_PERMISSION_DENIED,
		                             "Unable to determine UID of request.");
		goto error;
	}

336
	if (!nm_auth_is_subject_in_acl_set_error (nm_settings_connection_get_connection (sett_conn),
337 338 339 340
	                                          subject,
	                                          NM_SETTINGS_ERROR,
	                                          NM_SETTINGS_ERROR_PERMISSION_DENIED,
	                                          &error))
341
		goto error;
342

343 344
	g_dbus_method_invocation_return_value (invocation,
	                                       g_variant_new ("(o)",
345
	                                                      nm_dbus_object_get_path (NM_DBUS_OBJECT (sett_conn))));
346 347 348
	return;

error:
349
	g_dbus_method_invocation_take_error (invocation, error);
350 351
}

352
static void
353
_clear_connections_cached_list (NMSettingsPrivate *priv)
354
{
355 356 357 358 359
	if (!priv->connections_cached_list)
		return;

	nm_assert (priv->connections_len == NM_PTRARRAY_LEN (priv->connections_cached_list));

360 361 362 363 364
#if NM_MORE_ASSERTS
	/* set the pointer to a bogus value. This makes it more apparent
	 * if somebody has a reference to the cached list and still uses
	 * it. That is a bug, this code just tries to make it blow up
	 * more eagerly. */
365 366 367
	memset (priv->connections_cached_list,
	        0xdeaddead,
	        sizeof (NMSettingsConnection *) * (priv->connections_len + 1));
368
#endif
369

370
	nm_clear_g_free (&priv->connections_cached_list);
371 372
}

373 374 375
/**
 * nm_settings_get_connections:
 * @self: the #NMSettings
376
 * @out_len: (out) (allow-none): returns the number of returned
377 378
 *   connections.
 *
379
 * Returns: (transfer none): a list of NMSettingsConnections. The list is
380 381 382 383 384 385 386 387 388 389 390
 * unsorted and NULL terminated. The result is never %NULL, in case of no
 * connections, it returns an empty list.
 * The returned list is cached internally, only valid until the next
 * NMSettings operation.
 */
NMSettingsConnection *const*
nm_settings_get_connections (NMSettings *self, guint *out_len)
{
	NMSettingsPrivate *priv;
	NMSettingsConnection **v;
	NMSettingsConnection *con;
391
	guint i;
392 393 394 395 396

	g_return_val_if_fail (NM_IS_SETTINGS (self), NULL);

	priv = NM_SETTINGS_GET_PRIVATE (self);

397
	nm_assert (priv->connections_len == c_list_length (&priv->connections_lst_head));
398

399 400
	if (G_UNLIKELY (!priv->connections_cached_list)) {
		v = g_new (NMSettingsConnection *, priv->connections_len + 1);
401

402 403 404 405 406 407 408
		i = 0;
		c_list_for_each_entry (con, &priv->connections_lst_head, _connections_lst) {
			nm_assert (i < priv->connections_len);
			v[i++] = con;
		}
		nm_assert (i == priv->connections_len);
		v[i] = NULL;
409

410
		priv->connections_cached_list = v;
411
	}
412

413 414
	NM_SET_OUT (out_len, priv->connections_len);
	return priv->connections_cached_list;
415 416
}

417 418 419 420 421 422
/**
 * nm_settings_get_connections_clone:
 * @self: the #NMSetting
 * @out_len: (allow-none): optional output argument
 * @func: caller-supplied function for filtering connections
 * @func_data: caller-supplied data passed to @func
423 424 425
 * @sort_compare_func: (allow-none): optional function pointer for
 *   sorting the returned list.
 * @sort_data: user data for @sort_compare_func.
426 427 428 429 430 431 432 433 434 435 436 437
 *
 * Returns: (transfer container) (element-type NMSettingsConnection):
 *   an NULL terminated array of #NMSettingsConnection objects that were
 *   filtered by @func (or all connections if no filter was specified).
 *   The order is arbitrary.
 *   Caller is responsible for freeing the returned array with free(),
 *   the contained values do not need to be unrefed.
 */
NMSettingsConnection **
nm_settings_get_connections_clone (NMSettings *self,
                                   guint *out_len,
                                   NMSettingsConnectionFilterFunc func,
438 439 440
                                   gpointer func_data,
                                   GCompareDataFunc sort_compare_func,
                                   gpointer sort_data)
441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467
{
	NMSettingsConnection *const*list_cached;
	NMSettingsConnection **list;
	guint len, i, j;

	g_return_val_if_fail (NM_IS_SETTINGS (self), NULL);

	list_cached = nm_settings_get_connections (self, &len);

#if NM_MORE_ASSERTS
	nm_assert (list_cached);
	for (i = 0; i < len; i++)
		nm_assert (NM_IS_SETTINGS_CONNECTION (list_cached[i]));
	nm_assert (!list_cached[i]);
#endif

	list = g_new (NMSettingsConnection *, ((gsize) len + 1));
	if (func) {
		for (i = 0, j = 0; i < len; i++) {
			if (func (self, list_cached[i], func_data))
				list[j++] = list_cached[i];
		}
		list[j] = NULL;
		len = j;
	} else
		memcpy (list, list_cached, sizeof (list[0]) * ((gsize) len + 1));

468 469 470 471 472
	if (   len > 1
	    && sort_compare_func) {
		g_qsort_with_data (list, len, sizeof (NMSettingsConnection *),
		                   sort_compare_func, sort_data);
	}
473 474 475 476
	NM_SET_OUT (out_len, len);
	return list;
}

477
NMSettingsConnection *
478
nm_settings_get_connection_by_path (NMSettings *self, const char *path)
479
{
480
	NMSettingsPrivate *priv;
481
	NMSettingsConnection *connection;
482

483
	g_return_val_if_fail (NM_IS_SETTINGS (self), NULL);
484
	g_return_val_if_fail (path, NULL);
485

486
	priv = NM_SETTINGS_GET_PRIVATE (self);
487

488 489
	connection = nm_dbus_manager_lookup_object (nm_dbus_object_get_manager (NM_DBUS_OBJECT (self)),
	                                            path);
490 491 492 493 494 495
	if (   !connection
	    || !NM_IS_SETTINGS_CONNECTION (connection))
		return NULL;

	nm_assert (c_list_contains (&priv->connections_lst_head, &connection->_connections_lst));
	return connection;
496 497
}

498
gboolean
499
nm_settings_has_connection (NMSettings *self, NMSettingsConnection *connection)
500
{
501
	gboolean has;
502

503 504
	g_return_val_if_fail (NM_IS_SETTINGS (self), FALSE);
	g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (connection), FALSE);
505

506
	has = !c_list_is_empty (&connection->_connections_lst);
507

508 509 510 511 512 513 514 515 516 517 518 519 520 521 522
	nm_assert (has == nm_c_list_contains_entry (&NM_SETTINGS_GET_PRIVATE (self)->connections_lst_head,
                                                connection,
                                                _connections_lst));
	nm_assert (({
		NMSettingsConnection *candidate = NULL;
		const char *path;

		path = nm_dbus_object_get_path (NM_DBUS_OBJECT (connection));
		if (path)
			candidate = nm_settings_get_connection_by_path (self, path);

		(has == (connection == candidate));
	}));

	return has;
523 524
}

525
const GSList *
526
nm_settings_get_unmanaged_specs (NMSettings *self)
527
{
528
	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
529

530
	return priv->unmanaged_specs;
531 532
}

533
static gboolean
534
find_spec (GSList *spec_list, const char *spec)
535 536 537
{
	GSList *iter;

538 539
	for (iter = spec_list; iter; iter = g_slist_next (iter)) {
		if (!strcmp ((const char *) iter->data, spec))
540 541 542 543 544
			return TRUE;
	}
	return FALSE;
}

545
static void
546
update_specs (NMSettings *self, GSList **specs_ptr,
547
              GSList * (*get_specs_func) (NMSettingsPlugin *))
548
{
549
	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
550 551
	GSList *iter;

552 553
	g_slist_free_full (*specs_ptr, g_free);
	*specs_ptr = NULL;
554

555
	for (iter = priv->plugins; iter; iter = g_slist_next (iter)) {
556 557
		GSList *specs, *specs_iter;

558
		specs = get_specs_func (NM_SETTINGS_PLUGIN (iter->data));
559
		for (specs_iter = specs; specs_iter; specs_iter = specs_iter->next) {
560 561
			if (!find_spec (*specs_ptr, (const char *) specs_iter->data)) {
				*specs_ptr = g_slist_prepend (*specs_ptr, specs_iter->data);
562
			} else
563
				g_free (specs_iter->data);
564
		}
565

566
		g_slist_free (specs);
567
	}
568
}
569

570
static void
571
unmanaged_specs_changed (NMSettingsPlugin *config,
572 573 574 575 576 577
                         gpointer user_data)
{
	NMSettings *self = NM_SETTINGS (user_data);
	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);

	update_specs (self, &priv->unmanaged_specs,
578
	              nm_settings_plugin_get_unmanaged_specs);
579
	_notify (self, PROP_UNMANAGED_SPECS);
580
}
581

582
static void
583
unrecognized_specs_changed (NMSettingsPlugin *config,
584 585 586 587 588 589
                               gpointer user_data)
{
	NMSettings *self = NM_SETTINGS (user_data);
	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);

	update_specs (self, &priv->unrecognized_specs,
590
	              nm_settings_plugin_get_unrecognized_specs);
591 592
}

593 594
static void
add_plugin (NMSettings *self, NMSettingsPlugin *plugin, const char *path)
595
{
596
	NMSettingsPrivate *priv;
597

598 599
	nm_assert (NM_IS_SETTINGS (self));
	nm_assert (NM_IS_SETTINGS_PLUGIN (plugin));
600

601
	priv = NM_SETTINGS_GET_PRIVATE (self);
602

603
	nm_assert (!g_slist_find (priv->plugins, plugin));
604

605 606
	priv->plugins = g_slist_append (priv->plugins, g_object_ref (plugin));

607
	nm_settings_plugin_initialize (plugin);
608

609 610 611
	_LOGI ("Loaded settings plugin: %s (%s%s%s)",
	       G_OBJECT_TYPE_NAME (plugin),
	       NM_PRINT_FMT_QUOTED (path, "\"", path, "\"", "internal"));
612 613
}

614
static gboolean
615
add_plugin_load_file (NMSettings *self, const char *pname, GError **error)
616
{
617
	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
618 619
	gs_free char *full_name = NULL;
	gs_free char *path = NULL;
620 621 622 623
	gs_unref_object NMSettingsPlugin *plugin = NULL;
	GModule *module;
	NMSettingsPluginFactoryFunc factory_func;
	GSList *iter;
624 625 626 627 628 629
	struct stat st;
	int errsv;

	full_name = g_strdup_printf ("nm-settings-plugin-%s", pname);
	path = g_module_build_path (NMPLUGINDIR, full_name);

630 631 632 633 634 635
	for (iter = priv->plugins; iter; iter = iter->next) {
		if (nm_streq0 (path,
		               g_object_get_qdata (iter->data,
		                                   plugin_module_path_quark ())))
			return TRUE;
	}
636

637 638
	if (stat (path, &st) != 0) {
		errsv = errno;
639
		_LOGW ("could not load plugin '%s' from file '%s': %s", pname, path, nm_strerror_native (errsv));
640 641 642 643 644 645 646 647 648 649 650 651 652 653 654
		return TRUE;
	}
	if (!S_ISREG (st.st_mode)) {
		_LOGW ("could not load plugin '%s' from file '%s': not a file", pname, path);
		return TRUE;
	}
	if (st.st_uid != 0) {
		_LOGW ("could not load plugin '%s' from file '%s': file must be owned by root", pname, path);
		return TRUE;
	}
	if (st.st_mode & (S_IWGRP | S_IWOTH | S_ISUID)) {
		_LOGW ("could not load plugin '%s' from file '%s': invalid file permissions", pname, path);
		return TRUE;
	}

655 656
	module = g_module_open (path, G_MODULE_BIND_LOCAL);
	if (!module) {
657 658 659 660 661 662 663
		_LOGW ("could not load plugin '%s' from file '%s': %s",
		     pname, path, g_module_error ());
		return TRUE;
	}

	/* errors after this point are fatal, because we loaded the shared library already. */

664
	if (!g_module_symbol (module, "nm_settings_plugin_factory", (gpointer) (&factory_func))) {
665 666 667
		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
		             "Could not find plugin '%s' factory function.",
		             pname);
668
		g_module_close (module);
669 670 671 672 673
		return FALSE;
	}

	/* after accessing the plugin we cannot unload it anymore, because the glib
	 * types cannot be properly unregistered. */
674
	g_module_make_resident (module);
675

676 677
	plugin = (*factory_func) ();
	if (!NM_IS_SETTINGS_PLUGIN (plugin)) {
678
		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
679
		             "plugin '%s' returned invalid settings plugin",
680 681 682 683
		             pname);
		return FALSE;
	}

684 685 686 687 688
	add_plugin (self, NM_SETTINGS_PLUGIN (plugin), path);
	g_object_set_qdata_full (G_OBJECT (plugin),
	                         plugin_module_path_quark (),
	                         g_steal_pointer (&path),
	                         g_free);
689 690 691
	return TRUE;
}

692
static void
693
add_plugin_keyfile (NMSettings *self)
694
{
695
	gs_unref_object NMSKeyfilePlugin *keyfile_plugin = NULL;
696

697
	keyfile_plugin = nms_keyfile_plugin_new ();
698
	add_plugin (self, NM_SETTINGS_PLUGIN (keyfile_plugin), NULL);
699 700
}

701
static gboolean
702
load_plugins (NMSettings *self, const char **plugins, GError **error)
703
{
704
	const char **iter;
705
	gboolean keyfile_added = FALSE;
706
	gboolean success = TRUE;
707 708 709 710
	gboolean add_ibft = FALSE;
	gboolean has_no_ibft;
	gssize idx_no_ibft, idx_ibft;

711 712
	idx_ibft    = nm_utils_strv_find_first ((char **) plugins, -1, "ibft");
	idx_no_ibft = nm_utils_strv_find_first ((char **) plugins, -1, "no-ibft");
713 714 715 716
	has_no_ibft = idx_no_ibft >= 0 && idx_no_ibft > idx_ibft;
#if WITH_SETTINGS_PLUGIN_IBFT
	add_ibft = idx_no_ibft < 0 && idx_ibft < 0;
#endif
717

718
	for (iter = plugins; iter && *iter; iter++) {
719
		const char *pname = *iter;
720

721
		if (!*pname || strchr (pname, '/')) {
722
			_LOGW ("ignore invalid plugin \"%s\"", pname);
723 724
			continue;
		}
725

726 727
		if (NM_IN_STRSET (pname, "ifcfg-suse", "ifnet")) {
			_LOGW ("skipping deprecated plugin %s", pname);
728 729 730
			continue;
		}

731
		if (nm_streq (pname, "no-ibft"))
732
			continue;
733
		if (has_no_ibft && nm_streq (pname, "ibft"))
734 735
			continue;

736
		/* keyfile plugin is built-in now */
737
		if (nm_streq (pname, "keyfile")) {
738
			if (!keyfile_added) {
739
				add_plugin_keyfile (self);
740 741 742 743 744
				keyfile_added = TRUE;
			}
			continue;
		}

745 746 747
		if (nm_utils_strv_find_first ((char **) plugins,
		                              iter - plugins,
		                              pname) >= 0) {
748 749 750 751 752
			/* the plugin is already mentioned in the list previously.
			 * Don't load a duplicate. */
			continue;
		}

753
		success = add_plugin_load_file (self, pname, error);
754 755
		if (!success)
			break;
756

757
		if (add_ibft && nm_streq (pname, "ifcfg-rh")) {
758 759 760 761
			/* The plugin ibft is not explicitly mentioned but we just enabled "ifcfg-rh".
			 * Enable "ibft" by default after "ifcfg-rh". */
			pname = "ibft";
			add_ibft = FALSE;
762

763
			success = add_plugin_load_file (self, "ibft", error);
764 765
			if (!success)
				break;
766 767 768
		}
	}

769
	/* If keyfile plugin was not among configured plugins, add it as the last one */
770 771
	if (!keyfile_added && success)
		add_plugin_keyfile (self);
772 773 774 775

	return success;
}

776
static void
777
connection_updated (NMSettingsConnection *connection, gboolean by_user, gpointer user_data)
778
{
779
	g_signal_emit (NM_SETTINGS (user_data),
780 781
	               signals[CONNECTION_UPDATED],
	               0,
782 783
	               connection,
	               by_user);
784 785
}

786 787 788 789 790 791 792 793 794 795
static void
connection_flags_changed (NMSettingsConnection *connection,
                          gpointer user_data)
{
	g_signal_emit (NM_SETTINGS (user_data),
	               signals[CONNECTION_FLAGS_CHANGED],
	               0,
	               connection);
}

796 797 798 799
static void
connection_removed (NMSettingsConnection *connection, gpointer user_data)
{
	NMSettings *self = NM_SETTINGS (user_data);
800
	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
801
	NMDevice *device;
802

803 804 805
	g_return_if_fail (NM_IS_SETTINGS_CONNECTION (connection));
	g_return_if_fail (!c_list_is_empty (&connection->_connections_lst));
	nm_assert (c_list_contains (&priv->connections_lst_head, &connection->_connections_lst));
806

807 808 809 810 811 812 813 814 815
	/* When the default wired connection is removed (either deleted or saved to
	 * a new persistent connection by a plugin), write the MAC address of the
	 * wired device to the config file and don't create a new default wired
	 * connection for that device again.
	 */
	device = g_object_get_qdata (G_OBJECT (connection), _default_wired_device_quark ());
	if (device)
		default_wired_clear_tag (self, device, connection, TRUE);

816 817 818 819 820 821 822
	/* Disconnect signal handlers, as plugins might still keep references
	 * to the connection (and thus the signal handlers would still be live)
	 * even after NMSettings has dropped all its references.
	 */

	g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (connection_removed), self);
	g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (connection_updated), self);
823
	g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (connection_flags_changed), self);
824 825
	if (!priv->startup_complete)
		g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (connection_ready_changed), self);
826

827 828 829 830
	/* Forget about the connection internally */
	_clear_connections_cached_list (priv);
	priv->connections_len--;
	c_list_unlink (&connection->_connections_lst);
831

832 833 834 835 836 837 838 839 840 841 842 843 844 845 846
	if (priv->connections_loaded) {
		_notify (self, PROP_CONNECTIONS);

		nm_dbus_object_emit_signal (NM_DBUS_OBJECT (self),
		                            &interface_info_settings,
		                            &signal_info_connection_removed,
		                            "(o)",
		                            nm_dbus_object_get_path (NM_DBUS_OBJECT (connection)));
	}

	nm_dbus_object_unexport (NM_DBUS_OBJECT (connection));

	if (priv->connections_loaded)
		g_signal_emit (self, signals[CONNECTION_REMOVED], 0, connection);

847
	check_startup_complete (self);
848

849 850
	g_object_unref (connection);

851
	g_object_unref (self);       /* Balanced by a ref in claim_connection() */
852 853
}

854 855 856 857
#define NM_DBUS_SERVICE_OPENCONNECT    "org.freedesktop.NetworkManager.openconnect"
#define NM_OPENCONNECT_KEY_GATEWAY "gateway"
#define NM_OPENCONNECT_KEY_COOKIE "cookie"
#define NM_OPENCONNECT_KEY_GWCERT "gwcert"
858 859 860 861
#define NM_OPENCONNECT_KEY_XMLCONFIG "xmlconfig"
#define NM_OPENCONNECT_KEY_LASTHOST "lasthost"
#define NM_OPENCONNECT_KEY_AUTOCONNECT "autoconnect"
#define NM_OPENCONNECT_KEY_CERTSIGS "certsigs"
862 863 864 865

static void
openconnect_migrate_hack (NMConnection *connection)
{
866
	NMSettingVpn *s_vpn;
867 868 869 870 871 872 873 874 875 876 877 878 879
	NMSettingSecretFlags flags = NM_SETTING_SECRET_FLAG_NOT_SAVED;

	/* Huge hack.  There were some openconnect changes that needed to happen
	 * pretty late, too late to get into distros.  Migration has already
	 * happened for many people, and their secret flags are wrong.  But we
	 * don't want to requrie re-migration, so we have to fix it up here. Ugh.
	 */

	s_vpn = nm_connection_get_setting_vpn (connection);
	if (s_vpn == NULL)
		return;

	if (g_strcmp0 (nm_setting_vpn_get_service_type (s_vpn), NM_DBUS_SERVICE_OPENCONNECT) == 0) {
880
		/* These are different for every login session, and should not be stored */
881 882 883
		nm_setting_set_secret_flags (NM_SETTING (s_vpn), NM_OPENCONNECT_KEY_GATEWAY, flags, NULL);
		nm_setting_set_secret_flags (NM_SETTING (s_vpn), NM_OPENCONNECT_KEY_COOKIE, flags, NULL);
		nm_setting_set_secret_flags (NM_SETTING (s_vpn), NM_OPENCONNECT_KEY_GWCERT, flags, NULL);
884 885 886 887 888 889 890

		/* These are purely internal data for the auth-dialog, and should be stored */
		flags = 0;
		nm_setting_set_secret_flags (NM_SETTING (s_vpn), NM_OPENCONNECT_KEY_XMLCONFIG, flags, NULL);
		nm_setting_set_secret_flags (NM_SETTING (s_vpn), NM_OPENCONNECT_KEY_LASTHOST, flags, NULL);
		nm_setting_set_secret_flags (NM_SETTING (s_vpn), NM_OPENCONNECT_KEY_AUTOCONNECT, flags, NULL);
		nm_setting_set_secret_flags (NM_SETTING (s_vpn), NM_OPENCONNECT_KEY_CERTSIGS, flags, NULL);
891 892 893
	}
}

894
static void
895
claim_connection (NMSettings *self, NMSettingsConnection *sett_conn)
896
{
897
	NMSettingsPrivate *priv;
898
	GError *error = NULL;
899
	const char *path;
900
	NMSettingsConnection *existing;
901

902 903 904 905 906
	g_return_if_fail (NM_IS_SETTINGS (self));
	g_return_if_fail (NM_IS_SETTINGS_CONNECTION (sett_conn));
	g_return_if_fail (!nm_dbus_object_is_exported (NM_DBUS_OBJECT (sett_conn)));

	priv = NM_SETTINGS_GET_PRIVATE (self);
907

908
	/* prevent duplicates */
909 910
	if (!c_list_is_empty (&sett_conn->_connections_lst)) {
		nm_assert (c_list_contains (&priv->connections_lst_head, &sett_conn->_connections_lst));
911
		return;
912 913
	}

914 915
	/* FIXME(copy-on-write-connection): avoid modifying NMConnection instances and share them via copy-on-write. */
	if (!nm_connection_normalize (nm_settings_connection_get_connection (sett_conn), NULL, NULL, &error)) {
916
		_LOGW ("plugin provided invalid connection: %s", error->message);
917
		g_error_free (error);
918
		return;
919 920
	}

921
	existing = nm_settings_get_connection_by_uuid (self, nm_settings_connection_get_uuid (sett_conn));
922 923 924 925 926 927 928 929 930 931 932
	if (existing) {
		/* Cannot add duplicate connections per UUID. Just return without action and
		 * log a warning.
		 *
		 * This means, that plugins must not provide duplicate connections (UUID).
		 * In fact, none of the plugins currently would do that.
		 *
		 * But globaly, over different setting plugins, there could be duplicates
		 * without the individual plugins being aware. Don't handle that at all, just
		 * error out. That should not happen unless the admin misconfigured the system
		 * to create conflicting connections. */
933
		_LOGW ("plugin provided duplicate connection with UUID %s",
934
		       nm_settings_connection_get_uuid (sett_conn));
935 936 937
		return;
	}

938
	/* Read timestamp from look-aside file and put it into the connection's data */
939
	nm_settings_connection_read_and_fill_timestamp (sett_conn);
940

941
	/* Read seen-bssids from look-aside file and put it into the connection's data */
942
	nm_settings_connection_read_and_fill_seen_bssids (sett_conn);
943

944
	/* Ensure its initial visibility is up-to-date */
945
	nm_settings_connection_recheck_visibility (sett_conn);
946

947
	/* Evil openconnect migration hack */
948 949
	/* FIXME(copy-on-write-connection): avoid modifying NMConnection instances and share them via copy-on-write. */
	openconnect_migrate_hack (nm_settings_connection_get_connection (sett_conn));
950

951 952
	/* This one unexports the connection, it needs to run late to give the active
	 * connection a chance to deal with its reference to this settings connection. */
953
	g_signal_connect_after (sett_conn, NM_SETTINGS_CONNECTION_REMOVED,
954
	                        G_CALLBACK (connection_removed), self);
955
	g_signal_connect (sett_conn, NM_SETTINGS_CONNECTION_UPDATED_INTERNAL,
956
	                  G_CALLBACK (connection_updated), self);
957
	g_signal_connect (sett_conn, NM_SETTINGS_CONNECTION_FLAGS_CHANGED,
958 959
	                  G_CALLBACK (connection_flags_changed),
	                  self);
960
	if (!priv->startup_complete) {
961
		g_signal_connect (sett_conn, "notify::" NM_SETTINGS_CONNECTION_READY,
962 963 964
		                  G_CALLBACK (connection_ready_changed),
		                  self);
	}
965

966 967
	_clear_connections_cached_list (priv);

968
	g_object_ref (sett_conn);
969 970 971 972 973
	/* FIXME(shutdown): The NMSettings instance can't be disposed
	 * while there is any exported connection. Ideally we should
	 * unexport all connections on NMSettings' disposal, but for now
	 * leak @self on termination when there are connections alive. */
	g_object_ref (self);
974
	priv->connections_len++;
975
	c_list_link_tail (&priv->connections_lst_head, &sett_conn->_connections_lst);
976

977
	path = nm_dbus_object_export (NM_DBUS_OBJECT (sett_conn));
978

979 980 981 982 983
	nm_utils_log_connection_diff (nm_settings_connection_get_connection (sett_conn),
	                              NULL,
	                              LOGL_DEBUG,
	                              LOGD_CORE,
	                              "new connection", "++ ",
984
	                              path);
985

986
	/* Only emit the individual connection-added signal after connections
987
	 * have been initially loaded.
988
	 */
989
	if (priv->connections_loaded) {
990 991 992 993
		nm_dbus_object_emit_signal (NM_DBUS_OBJECT (self),
		                            &interface_info_settings,
		                            &signal_info_new_connection,
		                            "(o)",
994
		                            nm_dbus_object_get_path (NM_DBUS_OBJECT (sett_conn)));
995

996
		g_signal_emit (self, signals[CONNECTION_ADDED], 0, sett_conn);
997
		_notify (self, PROP_CONNECTIONS);
998
	}
999

1000
	nm_settings_connection_added (sett_conn);
1001 1002
}

1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021
static gboolean
secrets_filter_cb (NMSetting *setting,
                   const char *secret,
                   NMSettingSecretFlags flags,
                   gpointer user_data)
{
	NMSettingSecretFlags filter_flags = GPOINTER_TO_UINT (user_data);

	/* Returns TRUE to remove the secret */

	/* Can't use bitops with SECRET_FLAG_NONE so handle that specifically */
	if (   (flags == NM_SETTING_SECRET_FLAG_NONE)
	    && (filter_flags == NM_SETTING_SECRET_FLAG_NONE))
		return FALSE;

	/* Otherwise if the secret has at least one of the desired flags keep it */
	return (flags & filter_flags) ? FALSE : TRUE;
}

1022 1023 1024 1025 1026 1027 1028 1029
/**
 * nm_settings_add_connection:
 * @self: the #NMSettings object
 * @connection: the source connection to create a new #NMSettingsConnection from
 * @save_to_disk: %TRUE to save the connection to disk immediately, %FALSE to
 * not save to disk
 * @error: on return, a location to store any errors that may occur
 *
1030
 * Creates a new #NMSettingsConnection for the given source @connection.
1031 1032 1033 1034 1035
 * The returned object is owned by @self and the caller must reference
 * the object to continue using it.
 *
 * Returns: the new #NMSettingsConnection or %NULL
 */
1036
NMSettingsConnection *
1037 1038 1039 1040
nm_settings_add_connection (NMSettings *self,
                            NMConnection *connection,
                            gboolean save_to_disk,
                            GError **error)
1041
{
1042
	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
1043
	GSList *iter;
1044
	NMSettingsConnection *added = NULL;
1045 1046 1047 1048
	NMSettingsConnection *candidate = NULL;
	const char *uuid;

	uuid = nm_connection_get_uuid (connection);
1049 1050

	/* Make sure a connection with this UUID doesn't already exist */
1051
	c_list_for_each_entry (candidate, &priv->connections_lst_head, _connections_lst) {
1052
		if (nm_streq0 (uuid, nm_settings_connection_get_uuid (candidate))) {
1053 1054 1055 1056 1057 1058 1059
			g_set_error_literal (error,
			                     NM_SETTINGS_ERROR,
			                     NM_SETTINGS_ERROR_UUID_EXISTS,
			                     "A connection with this UUID already exists.");
			return NULL;
		}
	}
1060 1061

	/* 1) plugin writes the NMConnection to disk
1062
	 * 2) plugin creates a new NMSettingsConnection subclass with the settings
1063
	 *     from the NMConnection and returns it to the settings service
1064
	 * 3) settings service exports the new NMSettingsConnection subclass
1065 1066 1067 1068
	 * 4) plugin notices that something on the filesystem has changed
	 * 5) plugin reads the changes and ignores them because they will
	 *     contain the same data as the connection it already knows about
	 */
1069
	for (iter = priv->plugins; iter; iter = g_slist_next (iter)) {
1070
		NMSettingsPlugin *plugin = NM_SETTINGS_PLUGIN (iter->data);
1071
		GError *add_error = NULL;
1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082
		gs_unref_object NMConnection *simple = NULL;
		gs_unref_variant GVariant *secrets = NULL;

		/* Make a copy of agent-owned secrets because they won't be present in
		 * the connection returned by plugins, as plugins return only what was
		 * reread from the file. */
		simple = nm_simple_connection_new_clone (connection);
		nm_connection_clear_secrets_with_flags (simple,
		                                        secrets_filter_cb,
		                                        GUINT_TO_POINTER (NM_SETTING_SECRET_FLAG_AGENT_OWNED));
		secrets = nm_connection_to_dbus (simple, NM_CONNECTION_SERIALIZE_ONLY_SECRETS);
1083

1084
		added = nm_settings_plugin_add_connection (plugin, connection, save_to_disk, &add_error);
1085
		if (added) {
1086 1087 1088 1089 1090 1091 1092
			if (secrets) {
				/* FIXME(copy-on-write-connection): avoid modifying NMConnection instances and share them via copy-on-write. */
				nm_connection_update_secrets (nm_settings_connection_get_connection (added),
				                              NULL,
				                              secrets,
				                              NULL);
			}
1093
			claim_connection (self, added);
1094 1095
			return added;
		}
1096 1097 1098 1099
		_LOGD ("Failed to add %s/'%s': %s",
		       nm_connection_get_uuid (connection),
		       nm_connection_get_id (connection),
		       add_error->message);
1100
		g_clear_error (&add_error);
1101
	}
1102

1103
	g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
1104
	                     "No plugin supported adding this connection");
1105
	return NULL;
1106
}
1107

1108 1109
static void
send_agent_owned_secrets (NMSettings *self,
1110
                          NMSettingsConnection *sett_conn,
1111
                          NMAuthSubject *subject)
1112 1113
{
	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
1114
	gs_unref_object NMConnection *for_agent = NULL;
1115 1116 1117 1118 1119

	/* Dupe the connection so we can clear out non-agent-owned secrets,
	 * as agent-owned secrets are the only ones we send back to be saved.
	 * Only send secrets to agents of the same UID that called update too.
	 */
1120
	for_agent = nm_simple_connection_new_clone (nm_settings_connection_get_connection (sett_conn));
1121 1122 1123
	nm_connection_clear_secrets_with_flags (for_agent,
	                                        secrets_filter_cb,
	                                        GUINT_TO_POINTER (NM_SETTING_SECRET_FLAG_AGENT_OWNED));
1124
	nm_agent_manager_save_secrets (priv->agent_mgr,
1125
	                               nm_dbus_object_get_path (NM_DBUS_OBJECT (sett_conn)),
1126 1127
	                               for_agent,
	                               subject);
1128 1129
}

1130
static void
1131 1132
pk_add_cb (NMAuthChain *chain,
           GError *chain_error,
Dan Winship's avatar
Dan Winship committed
1133
           GDBusMethodInvocation *context,
1134
           gpointer user_data)
1135
{
1136 1137 1138
	NMSettings *self = NM_SETTINGS (user_data);
	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
	NMAuthCallResult result;
1139
	GError *error = NULL;
1140
	NMConnection *connection = NULL;
1141
	gs_unref_object NMSettingsConnection *added = NULL;
1142 1143
	NMSettingsAddCallback callback;
	gpointer callback_data;
1144
	NMAuthSubject *subject;
1145
	const char *perm;
1146
	gboolean save_to_disk;
1147

1148 1149
	g_assert (context);

1150
	priv->auths = g_slist_remove (priv->auths, chain);
1151

1152 1153 1154 1155
	perm = nm_auth_chain_get_data (chain, "perm");
	g_assert (perm);
	result = nm_auth_chain_get_result (chain, perm);

1156 1157
	if (chain_error) {
		error = g_error_new (NM_SETTINGS_ERROR,
1158
		                     NM_SETTINGS_ERROR_FAILED,
1159
		                     "Error checking authorization: %s",
1160
		                     chain_error->message);
1161
	} else if (result != NM_AUTH_CALL_RESULT_YES) {
1162
		error = g_error_new_literal (NM_SETTINGS_ERROR,
1163
		                             NM_SETTINGS_ERROR_PERMISSION_DENIED,
1164
		                             "Insufficient privileges.");