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