static gboolean
check_ip_routes (NMConnection *orig,
                 NMConnection *candidate,
                 GHashTable *settings,
                 gint64 default_metric,
                 gboolean v4)
{
	gs_free NMIPRoute **routes1 = NULL, **routes2 = NULL;
	NMSettingIPConfig *s_ip1, *s_ip2;
	const char *s_name;
	GHashTable *props;
	guint i, num;

	s_name = v4 ? NM_SETTING_IP4_CONFIG_SETTING_NAME :
	              NM_SETTING_IP6_CONFIG_SETTING_NAME;

	props = check_property_in_hash (settings,
	                                s_name,
	                                NM_SETTING_IP_CONFIG_ROUTES);
	if (!props)
		return TRUE;

	s_ip1 = (NMSettingIPConfig *) nm_connection_get_setting_by_name (orig, s_name);
	s_ip2 = (NMSettingIPConfig *) nm_connection_get_setting_by_name (candidate, s_name);

	if (!s_ip1 || !s_ip2)
		return FALSE;

	num = nm_setting_ip_config_get_num_routes (s_ip1);
	if (num != nm_setting_ip_config_get_num_routes (s_ip2))
		return FALSE;

	routes1 = g_new (NMIPRoute *, num);
	routes2 = g_new (NMIPRoute *, num);

	for (i = 0; i < num; i++) {
		routes1[i] = nm_setting_ip_config_get_route (s_ip1, i);
		routes2[i] = nm_setting_ip_config_get_route (s_ip2, i);
	}

	qsort (routes1, num, sizeof (NMIPRoute *), route_ptr_compare);
	qsort (routes2, num, sizeof (NMIPRoute *), route_ptr_compare);

	for (i = 0; i < num; i++) {
		if (route_compare (routes1[i], routes2[i], default_metric))
			return FALSE;
	}

	remove_from_hash (settings, props, s_name, NM_SETTING_IP_CONFIG_ROUTES);
	return TRUE;
}
예제 #2
0
static gboolean
extract_details_from_connection (NMConnection *connection,
                                 const char *secrets_setting_name,
                                 const char **username,
                                 const char **password,
                                 GError **error)
{
	NMSettingConnection *s_con;
	NMSetting *setting;
	const char *setting_name;

	g_return_val_if_fail (connection != NULL, FALSE);
	g_return_val_if_fail (username != NULL, FALSE);
	g_return_val_if_fail (password != NULL, FALSE);

	if (secrets_setting_name)
		setting_name = secrets_setting_name;
	else {
		/* Get the setting matching the connection type */
		s_con = nm_connection_get_setting_connection (connection);
		g_assert (s_con);

		setting_name = nm_setting_connection_get_connection_type (s_con);
		g_assert (setting_name);

		/* In case of bluetooth connection, use GSM or CDMA setting */
		if (strcmp (setting_name, NM_SETTING_BLUETOOTH_SETTING_NAME) == 0) {
			if (nm_connection_get_setting_gsm (connection))
				setting_name = NM_SETTING_GSM_SETTING_NAME;
			else
				setting_name = NM_SETTING_CDMA_SETTING_NAME;
		}
	}

	setting = nm_connection_get_setting_by_name (connection, setting_name);
	if (!setting) {
		/* This shouldn't ever happen */
		g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_FAILED,
		                     "Missing type-specific setting; no secrets could be found.");
		return FALSE;
	}

	if (NM_IS_SETTING_PPPOE (setting)) {
		*username = nm_setting_pppoe_get_username (NM_SETTING_PPPOE (setting));
		*password = nm_setting_pppoe_get_password (NM_SETTING_PPPOE (setting));
	} else if (NM_IS_SETTING_ADSL (setting)) {
		*username = nm_setting_adsl_get_username (NM_SETTING_ADSL (setting));
		*password = nm_setting_adsl_get_password (NM_SETTING_ADSL (setting));
	} else if (NM_IS_SETTING_GSM (setting)) {
		*username = nm_setting_gsm_get_username (NM_SETTING_GSM (setting));
		*password = nm_setting_gsm_get_password (NM_SETTING_GSM (setting));
	} else if (NM_IS_SETTING_CDMA (setting)) {
		*username = nm_setting_cdma_get_username (NM_SETTING_CDMA (setting));
		*password = nm_setting_cdma_get_password (NM_SETTING_CDMA (setting));
	}

	return TRUE;
}
예제 #3
0
static NMDevice *
find_vlan_parent (GSList *connections, NMApplet *applet)
{
	const GPtrArray *devices;
	NMDevice *parent_device;
	GSList *iter;

	devices = nm_client_get_devices (applet->nm_client);
	if (!devices)
		return NULL;

	for (iter = connections; iter; iter = iter->next) {
		NMConnection *connection = iter->data;
		NMSettingVlan *s_vlan;
		const char *parent;

		s_vlan = nm_connection_get_setting_vlan (connection);
		g_return_val_if_fail (s_vlan != NULL, NULL);

		parent = nm_setting_vlan_get_parent (s_vlan);
		if (parent && nm_utils_iface_valid_name (parent)) {
			parent_device = find_device_by_iface (parent, devices);
		} else {
			NMSettingConnection *s_con;
			NMSetting *s_hw;
			const char *type;
			GByteArray *mac;

			s_con = nm_connection_get_setting_connection (connection);
			type = nm_setting_connection_get_connection_type (s_con);
			s_hw = nm_connection_get_setting_by_name (connection, type);
			if (!s_hw) {
				g_warn_if_reached ();
				continue;
			}

			if (!g_object_class_find_property (G_OBJECT_GET_CLASS (s_hw),
			                                   "mac-address"))
				continue;

			g_object_get (G_OBJECT (s_hw),
			              "mac-address", &mac,
			              NULL);
			if (mac) {
				parent_device = find_device_by_mac (mac, devices);
				g_byte_array_unref (mac);
			} else
				parent_device = NULL;
		}
		
		if (parent_device)
			return parent_device;
	}

	return NULL;
}
static void
get_bt_secrets_cb (GtkDialog *dialog,
                   gint response,
                   gpointer user_data)
{
	SecretsRequest *req = user_data;
	NMBtSecretsInfo *info = (NMBtSecretsInfo *) req;
	NMSetting *setting;
	GError *error = NULL;

	if (response == GTK_RESPONSE_OK) {
		setting = nm_connection_get_setting_by_name (req->connection, req->setting_name);
		if (setting) {
			/* Normally we'd want to get all the settings's secrets and return those
			 * to NM too (since NM wants them), but since the only other secrets for 3G
			 * connections are PINs, and since the phone obviously has to be unlocked
			 * to even make the Bluetooth connection, we can skip doing that here for
			 * Bluetooth devices.
			 */

			/* Update the password */
			g_object_set (G_OBJECT (setting),
					      info->secret_name, gtk_entry_get_text (info->secret_entry),
					      NULL);
		} else {
			g_set_error (&error,
				         NM_SECRET_AGENT_ERROR,
				         NM_SECRET_AGENT_ERROR_FAILED,
				         "%s.%d (%s): unhandled setting '%s'",
				         __FILE__, __LINE__, __func__, req->setting_name);
		}
	} else {
		g_set_error (&error,
		             NM_SECRET_AGENT_ERROR,
		             NM_SECRET_AGENT_ERROR_FAILED,
		             "%s.%d (%s): canceled",
		             __FILE__, __LINE__, __func__);
	}

	applet_secrets_request_complete_setting (req, req->setting_name, error);
	applet_secrets_request_free (req);
	g_clear_error (&error);
}
static gboolean
is_connection_always_ask (NMConnection *connection)
{
	NMSettingConnection *s_con;
	const char *ctype;
	NMSetting *setting;

	/* For the given connection type, check if the secrets for that connection
	 * are always-ask or not.
	 */
	s_con = nm_connection_get_setting_connection (connection);
	g_assert (s_con);
	ctype = nm_setting_connection_get_connection_type (s_con);

	setting = nm_connection_get_setting_by_name (connection, ctype);
	g_return_val_if_fail (setting != NULL, FALSE);

	if (has_always_ask (setting))
		return TRUE;

	/* Try type-specific settings too; be a bit paranoid and only consider
	 * secrets from settings relevant to the connection type.
	 */
	if (NM_IS_SETTING_WIRELESS (setting)) {
		setting = nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS_SECURITY);
		if (setting && has_always_ask (setting))
			return TRUE;
		setting = nm_connection_get_setting (connection, NM_TYPE_SETTING_802_1X);
		if (setting && has_always_ask (setting))
			return TRUE;
	} else if (NM_IS_SETTING_WIRED (setting)) {
		setting = nm_connection_get_setting (connection, NM_TYPE_SETTING_PPPOE);
		if (setting && has_always_ask (setting))
			return TRUE;
		setting = nm_connection_get_setting (connection, NM_TYPE_SETTING_802_1X);
		if (setting && has_always_ask (setting))
			return TRUE;
	}

	return FALSE;
}
예제 #6
0
static gboolean
extract_details_from_connection (NMConnection *connection,
                                 const char **username,
                                 const char **password,
                                 GError **error)
{
	NMSettingConnection *s_con;
	NMSetting *setting;
	const char *connection_type;

	g_return_val_if_fail (connection != NULL, FALSE);
	g_return_val_if_fail (username != NULL, FALSE);
	g_return_val_if_fail (password != NULL, FALSE);

	s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION));
	g_assert (s_con);

	connection_type = nm_setting_connection_get_connection_type (s_con);
	g_assert (connection_type);

	setting = nm_connection_get_setting_by_name (connection, connection_type);
	if (!setting) {
		g_set_error_literal (error, NM_PPP_MANAGER_ERROR, NM_PPP_MANAGER_ERROR_UNKOWN,
		                     "Missing type-specific setting; no secrets could be found.");
		return FALSE;
	}

	/* FIXME: push this down to the settings and keep PPP manager generic */
	if (NM_IS_SETTING_PPPOE (setting)) {
		*username = nm_setting_pppoe_get_username (NM_SETTING_PPPOE (setting));
		*password = nm_setting_pppoe_get_password (NM_SETTING_PPPOE (setting));
	} else if (NM_IS_SETTING_GSM (setting)) {
		*username = nm_setting_gsm_get_username (NM_SETTING_GSM (setting));
		*password = nm_setting_gsm_get_password (NM_SETTING_GSM (setting));
	} else if (NM_IS_SETTING_CDMA (setting)) {
		*username = nm_setting_cdma_get_username (NM_SETTING_CDMA (setting));
		*password = nm_setting_cdma_get_password (NM_SETTING_CDMA (setting));
	}

	return TRUE;
}
static void
request_secrets_from_ui (NMSecretAgentSimpleRequest *request)
{
	GPtrArray *secrets;
	NMSecretAgentSimpleSecret *secret;
	const char *title;
	char *msg;
	gboolean ok = TRUE;

	secrets = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_secret_agent_simple_secret_free);

	if (nm_connection_is_type (request->connection, NM_SETTING_WIRELESS_SETTING_NAME)) {
		NMSettingWireless *s_wireless;
		GBytes *ssid;
		char *ssid_utf8;

		s_wireless = nm_connection_get_setting_wireless (request->connection);
		ssid = nm_setting_wireless_get_ssid (s_wireless);
		ssid_utf8 = nm_utils_ssid_to_utf8 (g_bytes_get_data (ssid, NULL),
		                                   g_bytes_get_size (ssid));

		title = _("Authentication required by wireless network");
		msg = g_strdup_printf (_("Passwords or encryption keys are required to access the wireless network '%s'."), ssid_utf8);

		ok = add_wireless_secrets (request, secrets);
	} else if (nm_connection_is_type (request->connection, NM_SETTING_WIRED_SETTING_NAME)) {
		NMSettingConnection *s_con;

		s_con = nm_connection_get_setting_connection (request->connection);

		title = _("Wired 802.1X authentication");
		msg = NULL;

		secret = nm_secret_agent_simple_secret_new (_("Network name"),
		                                            NM_SETTING (s_con),
		                                            NM_SETTING_CONNECTION_ID,
		                                            FALSE);
		g_ptr_array_add (secrets, secret);
		ok = add_8021x_secrets (request, secrets);
	} else if (nm_connection_is_type (request->connection, NM_SETTING_PPPOE_SETTING_NAME)) {
		title = _("DSL authentication");
		msg = NULL;

		ok = add_pppoe_secrets (request, secrets);
	} else if (nm_connection_is_type (request->connection, NM_SETTING_GSM_SETTING_NAME)) {
		NMSettingGsm *s_gsm = nm_connection_get_setting_gsm (request->connection);

		if (strv_has (request->hints, "pin")) {
			title = _("PIN code required");
			msg = g_strdup (_("PIN code is needed for the mobile broadband device"));

			secret = nm_secret_agent_simple_secret_new (_("PIN"),
			                                            NM_SETTING (s_gsm),
			                                            NM_SETTING_GSM_PIN,
			                                            FALSE);
			g_ptr_array_add (secrets, secret);
		} else {
			title = _("Mobile broadband network password");
			msg = g_strdup_printf (_("A password is required to connect to '%s'."),
			                       nm_connection_get_id (request->connection));

			secret = nm_secret_agent_simple_secret_new (_("Password"),
			                                            NM_SETTING (s_gsm),
			                                            NM_SETTING_GSM_PASSWORD,
			                                            TRUE);
			g_ptr_array_add (secrets, secret);
		}
	} else if (nm_connection_is_type (request->connection, NM_SETTING_CDMA_SETTING_NAME)) {
		NMSettingCdma *s_cdma = nm_connection_get_setting_cdma (request->connection);

		title = _("Mobile broadband network password");
		msg = g_strdup_printf (_("A password is required to connect to '%s'."),
		                       nm_connection_get_id (request->connection));

		secret = nm_secret_agent_simple_secret_new (_("Password"),
		                                            NM_SETTING (s_cdma),
		                                            NM_SETTING_CDMA_PASSWORD,
		                                            TRUE);
		g_ptr_array_add (secrets, secret);
	} else if (nm_connection_is_type (request->connection, NM_SETTING_BLUETOOTH_SETTING_NAME)) {
		NMSetting *setting;

		setting = nm_connection_get_setting_by_name (request->connection, NM_SETTING_GSM_SETTING_NAME);
		if (!setting)
			setting = nm_connection_get_setting_by_name (request->connection, NM_SETTING_CDMA_SETTING_NAME);

		title = _("Mobile broadband network password");
		msg = g_strdup_printf (_("A password is required to connect to '%s'."),
		                       nm_connection_get_id (request->connection));

		secret = nm_secret_agent_simple_secret_new (_("Password"),
		                                            setting,
		                                            "password",
		                                            TRUE);
		g_ptr_array_add (secrets, secret);
	} else
		ok = FALSE;

	if (!ok) {
		g_ptr_array_unref (secrets);
		return;
	}

	g_signal_emit (request->self, signals[REQUEST_SECRETS], 0,
	               request->request_id, title, msg, secrets);
}
static void
get_secrets (NMSecretAgent *agent,
             NMConnection *connection,
             const char *connection_path,
             const char *setting_name,
             const char **hints,
             guint32 flags,
             NMSecretAgentGetSecretsFunc callback,
             gpointer callback_data)
{
	AppletAgentPrivate *priv = APPLET_AGENT_GET_PRIVATE (agent);
	Request *r;
	GError *error = NULL;
	NMSettingConnection *s_con;
	NMSetting *setting;
	const char *uuid, *ctype;
	GHashTable *attrs;

	setting = nm_connection_get_setting_by_name (connection, setting_name);
	if (!setting) {
		error = g_error_new (NM_SECRET_AGENT_ERROR,
		                     NM_SECRET_AGENT_ERROR_INVALID_CONNECTION,
		                     "%s.%d - Connection didn't have requested setting '%s'.",
		                     __FILE__, __LINE__, setting_name);
		callback (agent, connection, NULL, error, callback_data);
		g_error_free (error);
		return;
	}

	uuid = nm_connection_get_uuid (connection);

	s_con = nm_connection_get_setting_connection (connection);
	g_assert (s_con);
	ctype = nm_setting_connection_get_connection_type (s_con);

	if (!uuid || !ctype) {
		error = g_error_new (NM_SECRET_AGENT_ERROR,
		                     NM_SECRET_AGENT_ERROR_INVALID_CONNECTION,
		                     "%s.%d - Connection didn't have required UUID.",
		                     __FILE__, __LINE__);
		callback (agent, connection, NULL, error, callback_data);
		g_error_free (error);
		return;
	}

	/* Track the secrets request */
	r = request_new (agent, connection, connection_path, setting_name, hints, flags, callback, NULL, NULL, callback_data);
	g_hash_table_insert (priv->requests, GUINT_TO_POINTER (r->id), r);

	/* VPN passwords are handled by the VPN plugin's auth dialog */
	if (!strcmp (ctype, NM_SETTING_VPN_SETTING_NAME)) {
		ask_for_secrets (r);
		return;
	}

	/* Only handle non-VPN secrets if we're supposed to */
	if (priv->vpn_only == TRUE) {
		error = g_error_new_literal (NM_SECRET_AGENT_ERROR,
		                             NM_SECRET_AGENT_ERROR_NO_SECRETS,
		                             "Only handling VPN secrets at this time.");
		callback (agent, connection, NULL, error, callback_data);
		g_error_free (error);
		return;
	}

	/* For everything else we scrape the keyring for secrets first, and ask
	 * later if required.
	 */
	attrs = secret_attributes_build (&network_manager_secret_schema,
	                                 KEYRING_UUID_TAG, uuid,
	                                 KEYRING_SN_TAG, setting_name,
	                                 NULL);

	secret_service_search (NULL, &network_manager_secret_schema, attrs,
	                       SECRET_SEARCH_ALL | SECRET_SEARCH_UNLOCK | SECRET_SEARCH_LOAD_SECRETS,
	                       r->cancellable, keyring_find_secrets_cb, r);

	r->keyring_calls++;
	g_hash_table_unref (attrs);
}
예제 #9
0
NMConnection *
nm_keyfile_plugin_connection_from_file (const char *filename, GError **error)
{
	GKeyFile *key_file;
	struct stat statbuf;
	gboolean bad_owner, bad_permissions;
	NMConnection *connection = NULL;
	NMSettingConnection *s_con;
	NMSetting *setting;
	gchar **groups;
	gsize length;
	int i;
	gboolean vpn_secrets = FALSE;
	const char *ctype;
	GError *verify_error = NULL;

	if (stat (filename, &statbuf) != 0 || !S_ISREG (statbuf.st_mode)) {
		g_set_error_literal (error, KEYFILE_PLUGIN_ERROR, 0,
		                     "File did not exist or was not a regular file");
		return NULL;
	}

	bad_owner = getuid () != statbuf.st_uid;
	bad_permissions = statbuf.st_mode & 0077;

	if (bad_owner || bad_permissions) {
		g_set_error (error, KEYFILE_PLUGIN_ERROR, 0,
		             "File permissions (%o) or owner (%d) were insecure",
		             statbuf.st_mode, statbuf.st_uid);
		return NULL;
	}

	key_file = g_key_file_new ();
	if (!g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, error))
		goto out;

	connection = nm_connection_new ();

	groups = g_key_file_get_groups (key_file, &length);
	for (i = 0; i < length; i++) {
		/* Only read out secrets when needed */
		if (!strcmp (groups[i], VPN_SECRETS_GROUP)) {
			vpn_secrets = TRUE;
			continue;
		}

		setting = read_setting (key_file, filename, groups[i]);
		if (setting)
			nm_connection_add_setting (connection, setting);
	}

	/* Make sure that we have the base device type setting even if
	 * the keyfile didn't include it, which can happen when the base
	 * device type setting is all default values (like ethernet).
	 */
	s_con = nm_connection_get_setting_connection (connection);
	if (s_con) {
		ctype = nm_setting_connection_get_connection_type (s_con);
		setting = nm_connection_get_setting_by_name (connection, ctype);
		if (ctype) {
			if (!setting && !strcmp (ctype, NM_SETTING_WIRED_SETTING_NAME))
				nm_connection_add_setting (connection, nm_setting_wired_new ());
		}
	}

	/* Handle vpn secrets after the 'vpn' setting was read */
	if (vpn_secrets) {
		NMSettingVPN *s_vpn;

		s_vpn = nm_connection_get_setting_vpn (connection);
		if (s_vpn)
			read_vpn_secrets (key_file, s_vpn);
	}

	g_strfreev (groups);

	/* Verify the connection */
	if (!nm_connection_verify (connection, &verify_error)) {
		g_set_error (error, KEYFILE_PLUGIN_ERROR, 0,
			         "invalid or missing connection property '%s/%s'",
			         verify_error ? g_type_name (nm_connection_lookup_setting_type_by_quark (verify_error->domain)) : "(unknown)",
			         (verify_error && verify_error->message) ? verify_error->message : "(unknown)");
		g_clear_error (&verify_error);
		g_object_unref (connection);
		connection = NULL;
		g_warning ("Connection failed to verify: %s",
			verify_error ? g_type_name (nm_connection_lookup_setting_type_by_quark (verify_error->domain)) : "(unknown)");
	}

out:
	g_key_file_free (key_file);
	return connection;
}
예제 #10
0
static void
get_secrets (NMSecretAgent *agent,
             NMConnection *connection,
             const char *connection_path,
             const char *setting_name,
             const char **hints,
             guint32 flags,
             NMSecretAgentGetSecretsFunc callback,
             gpointer callback_data)
{
	AppletAgentPrivate *priv = APPLET_AGENT_GET_PRIVATE (agent);
	Request *r;
	GError *error = NULL;
	NMSettingConnection *s_con;
	NMSetting *setting;
	const char *uuid, *ctype;
	KeyringCall *call;

	setting = nm_connection_get_setting_by_name (connection, setting_name);
	if (!setting) {
		error = g_error_new (NM_SECRET_AGENT_ERROR,
		                     NM_SECRET_AGENT_ERROR_INVALID_CONNECTION,
		                     "%s.%d - Connection didn't have requested setting '%s'.",
		                     __FILE__, __LINE__, setting_name);
		callback (agent, connection, NULL, error, callback_data);
		g_error_free (error);
		return;
	}

	uuid = nm_connection_get_uuid (connection);

	s_con = nm_connection_get_setting_connection (connection);
	g_assert (s_con);
	ctype = nm_setting_connection_get_connection_type (s_con);

	if (!uuid || !ctype) {
		error = g_error_new (NM_SECRET_AGENT_ERROR,
		                     NM_SECRET_AGENT_ERROR_INVALID_CONNECTION,
		                     "%s.%d - Connection didn't have required UUID.",
		                     __FILE__, __LINE__);
		callback (agent, connection, NULL, error, callback_data);
		g_error_free (error);
		return;
	}

	/* Track the secrets request */
	r = request_new (agent, connection, connection_path, setting_name, hints, flags, callback, NULL, NULL, callback_data);
	g_hash_table_insert (priv->requests, GUINT_TO_POINTER (r->id), r);

	/* VPN passwords are handled by the VPN plugin's auth dialog */
	if (!strcmp (ctype, NM_SETTING_VPN_SETTING_NAME)) {
		ask_for_secrets (r);
		return;
	}

	/* For everything else we scrape the keyring for secrets first, and ask
	 * later if required.
	 */
	call = keyring_call_new (r);
	call->keyring_id = gnome_keyring_find_itemsv (GNOME_KEYRING_ITEM_GENERIC_SECRET,
	                                              keyring_find_secrets_cb,
	                                              call,
	                                              keyring_call_free,
	                                              KEYRING_UUID_TAG,
	                                              GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
	                                              uuid,
	                                              KEYRING_SN_TAG,
	                                              GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
	                                              setting_name,
	                                              NULL);
	r->keyring_calls = g_slist_append (r->keyring_calls, call);
}
NMConnection *
connection_from_file (const char *filename, GError **error)
{
	GKeyFile *key_file;
	struct stat statbuf;
	gboolean bad_owner, bad_permissions;
	NMConnection *connection = NULL;
	NMSettingConnection *s_con;
	NMSettingBluetooth *s_bt;
	NMSetting *setting;
	gchar **groups;
	gsize length;
	int i;
	gboolean vpn_secrets = FALSE;
	const char *ctype, *tmp;
	GError *verify_error = NULL;

	if (stat (filename, &statbuf) != 0 || !S_ISREG (statbuf.st_mode)) {
		g_set_error_literal (error, KEYFILE_PLUGIN_ERROR, 0,
		                     "File did not exist or was not a regular file");
		return NULL;
	}

	bad_owner = getuid () != statbuf.st_uid;
	bad_permissions = statbuf.st_mode & 0077;

	if (bad_owner || bad_permissions) {
		g_set_error (error, KEYFILE_PLUGIN_ERROR, 0,
		             "File permissions (%o) or owner (%d) were insecure",
		             statbuf.st_mode, statbuf.st_uid);
		return NULL;
	}

	key_file = g_key_file_new ();
	if (!g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, error))
		goto out;

	connection = nm_connection_new ();

	groups = g_key_file_get_groups (key_file, &length);
	for (i = 0; i < length; i++) {
		/* Only read out secrets when needed */
		if (!strcmp (groups[i], VPN_SECRETS_GROUP)) {
			vpn_secrets = TRUE;
			continue;
		}

		setting = read_setting (key_file, groups[i]);
		if (setting)
			nm_connection_add_setting (connection, setting);
	}

	/* Make sure that we have the base device type setting even if
	 * the keyfile didn't include it, which can happen when the base
	 * device type setting is all default values (like ethernet).
	 */
	s_con = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION);
	if (s_con) {
		ctype = nm_setting_connection_get_connection_type (s_con);
		setting = nm_connection_get_setting_by_name (connection, ctype);
		if (ctype) {
			gboolean add_serial = FALSE;
			NMSetting *new_setting = NULL;

			if (!setting && !strcmp (ctype, NM_SETTING_WIRED_SETTING_NAME))
				new_setting = nm_setting_wired_new ();
			else if (!strcmp (ctype, NM_SETTING_BLUETOOTH_SETTING_NAME)) {
				s_bt = (NMSettingBluetooth *) nm_connection_get_setting (connection, NM_TYPE_SETTING_BLUETOOTH);
				if (s_bt) {
					tmp = nm_setting_bluetooth_get_connection_type (s_bt);
					if (tmp && !strcmp (tmp, NM_SETTING_BLUETOOTH_TYPE_DUN))
						add_serial = TRUE;
				}
			} else if (!strcmp (ctype, NM_SETTING_GSM_SETTING_NAME))
				add_serial = TRUE;
			else if (!strcmp (ctype, NM_SETTING_CDMA_SETTING_NAME))
				add_serial = TRUE;

			/* Bluetooth DUN, GSM, and CDMA connections require a serial setting */
			if (add_serial && !nm_connection_get_setting (connection, NM_TYPE_SETTING_SERIAL))
				new_setting = nm_setting_serial_new ();

			if (new_setting)
				nm_connection_add_setting (connection, new_setting);
		}
	}

	/* Serial connections require a PPP setting too */
	if (nm_connection_get_setting (connection, NM_TYPE_SETTING_SERIAL)) {
		if (!nm_connection_get_setting (connection, NM_TYPE_SETTING_PPP))
			nm_connection_add_setting (connection, nm_setting_ppp_new ());
	}

	/* Handle vpn secrets after the 'vpn' setting was read */
	if (vpn_secrets) {
		NMSettingVPN *s_vpn;

		s_vpn = (NMSettingVPN *) nm_connection_get_setting (connection, NM_TYPE_SETTING_VPN);
		if (s_vpn)
			read_vpn_secrets (key_file, s_vpn);
	}

	g_strfreev (groups);

	/* Verify the connection */
	if (!nm_connection_verify (connection, &verify_error)) {
		g_set_error (error, KEYFILE_PLUGIN_ERROR, 0,
			         "invalid or missing connection property '%s'",
			         (verify_error && verify_error->message) ? verify_error->message : "(unknown)");
		g_clear_error (&verify_error);
		g_object_unref (connection);
		connection = NULL;
	}

out:
	g_key_file_free (key_file);
	return connection;
}