diff options
Diffstat (limited to 'src/settings/plugins/ifnet/nms-ifnet-connection-parser.c')
| -rw-r--r-- | src/settings/plugins/ifnet/nms-ifnet-connection-parser.c | 2912 |
1 files changed, 2912 insertions, 0 deletions
diff --git a/src/settings/plugins/ifnet/nms-ifnet-connection-parser.c b/src/settings/plugins/ifnet/nms-ifnet-connection-parser.c new file mode 100644 index 00000000..ed0a757f --- /dev/null +++ b/src/settings/plugins/ifnet/nms-ifnet-connection-parser.c @@ -0,0 +1,2912 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * Mu Qiao <qiaomuf@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 1999-2010 Gentoo Foundation, Inc. + */ + +#include "nm-default.h" + +#include "nms-ifnet-connection-parser.h" + +#include <string.h> +#include <arpa/inet.h> +#include <stdlib.h> +#include <errno.h> + +#include "settings/nm-settings-plugin.h" +#include "nm-core-internal.h" +#include "NetworkManagerUtils.h" +#include "nm-meta-setting.h" + +#include "nms-ifnet-net-utils.h" +#include "nms-ifnet-wpa-parser.h" +#include "nms-ifnet-connection.h" + +static char * +connection_id_from_ifnet_name (const char *conn_name) +{ + int name_len = strlen (conn_name); + + /* Convert a hex-encoded conn_name (only used for wifi SSIDs) to human-readable one */ + if ((name_len > 2) && (g_str_has_prefix (conn_name, "0x"))) { + GBytes *bytes = nm_utils_hexstr2bin (conn_name); + char *buf; + + if (bytes) { + buf = g_strndup (g_bytes_get_data (bytes, NULL), g_bytes_get_size (bytes)); + g_bytes_unref (bytes); + return buf; + } + } + + return g_strdup (conn_name); +} + +static gboolean eap_simple_reader (const char *eap_method, + const char *ssid, + NMSetting8021x *s_8021x, + gboolean phase2, + const char *basepath, + GError **error); + +static gboolean eap_tls_reader (const char *eap_method, + const char *ssid, + NMSetting8021x *s_8021x, + gboolean phase2, + const char *basepath, + GError **error); + +static gboolean eap_peap_reader (const char *eap_method, + const char *ssid, + NMSetting8021x *s_8021x, + gboolean phase2, + const char *basepath, + GError **error); + +static gboolean eap_ttls_reader (const char *eap_method, + const char *ssid, + NMSetting8021x *s_8021x, + gboolean phase2, + const char *basepath, + GError **error); + +typedef struct { + const char *method; + gboolean (*reader) (const char *eap_method, + const char *ssid, + NMSetting8021x *s_8021x, + gboolean phase2, + const char *basepath, + GError **error); + gboolean wifi_phase2_only; +} EAPReader; + +static EAPReader eap_readers[] = { + {"md5", eap_simple_reader, TRUE}, + {"pwd", eap_simple_reader, TRUE}, + {"pap", eap_simple_reader, TRUE}, + {"chap", eap_simple_reader, TRUE}, + {"mschap", eap_simple_reader, TRUE}, + {"mschapv2", eap_simple_reader, TRUE}, + {"leap", eap_simple_reader, TRUE}, + {"tls", eap_tls_reader, FALSE}, + {"peap", eap_peap_reader, FALSE}, + {"ttls", eap_ttls_reader, FALSE}, + {NULL, NULL} +}; + +/* reading identity and password */ +static gboolean +eap_simple_reader (const char *eap_method, + const char *ssid, + NMSetting8021x *s_8021x, + gboolean phase2, + const char *basepath, + GError **error) +{ + const char *value; + + /* identity */ + value = wpa_get_value (ssid, "identity"); + if (!value) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Missing IEEE_8021X_IDENTITY for EAP method '%s'.", + eap_method); + return FALSE; + } + g_object_set (s_8021x, NM_SETTING_802_1X_IDENTITY, value, NULL); + + /* password */ + value = wpa_get_value (ssid, "password"); + if (!value) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Missing IEEE_8021X_PASSWORD for EAP method '%s'.", + eap_method); + return FALSE; + } + + g_object_set (s_8021x, NM_SETTING_802_1X_PASSWORD, value, NULL); + + return TRUE; +} + +static char * +get_cert (const char *ssid, const char *key, const char *basepath) +{ + const char *orig; + + /* If it's a relative path, convert to absolute using 'basepath' */ + orig = wpa_get_value (ssid, key); + if (g_path_is_absolute (orig)) + return g_strdup (orig); + return g_strdup_printf ("%s/%s", basepath, orig); +} + +static gboolean +eap_tls_reader (const char *eap_method, + const char *ssid, + NMSetting8021x *s_8021x, + gboolean phase2, + const char *basepath, + GError **error) +{ + const char *value; + char *ca_cert = NULL; + char *client_cert = NULL; + char *privkey = NULL; + const char *privkey_password = NULL; + gboolean success = FALSE; + NMSetting8021xCKFormat privkey_format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN; + + /* identity */ + value = wpa_get_value (ssid, "identity"); + if (!value) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Missing IEEE_8021X_IDENTITY for EAP method '%s'.", + eap_method); + return FALSE; + } + g_object_set (s_8021x, NM_SETTING_802_1X_IDENTITY, value, NULL); + + /* ca cert */ + ca_cert = get_cert (ssid, phase2 ? "ca_cert2" : "ca_cert", basepath); + if (ca_cert) { + if (phase2) { + if (!nm_setting_802_1x_set_phase2_ca_cert (s_8021x, + ca_cert, + NM_SETTING_802_1X_CK_SCHEME_PATH, + NULL, error)) + goto done; + } else { + if (!nm_setting_802_1x_set_ca_cert (s_8021x, + ca_cert, + NM_SETTING_802_1X_CK_SCHEME_PATH, + NULL, error)) + goto done; + } + } else { + nm_log_warn (LOGD_SETTINGS, " missing %s for EAP method '%s'; this is insecure!", + phase2 ? "IEEE_8021X_INNER_CA_CERT" : + "IEEE_8021X_CA_CERT", eap_method); + } + + /* Private key password */ + privkey_password = wpa_get_value (ssid, + phase2 ? "private_key2_passwd" : + "private_key_passwd"); + + if (!privkey_password) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Missing %s for EAP method '%s'.", + phase2 ? "IEEE_8021X_INNER_PRIVATE_KEY_PASSWORD" : + "IEEE_8021X_PRIVATE_KEY_PASSWORD", eap_method); + goto done; + } + + /* The private key itself */ + privkey = get_cert (ssid, phase2 ? "private_key2" : "private_key", basepath); + if (!privkey) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Missing %s for EAP method '%s'.", + phase2 ? "IEEE_8021X_INNER_PRIVATE_KEY" : + "IEEE_8021X_PRIVATE_KEY", eap_method); + goto done; + } + + if (phase2) { + if (!nm_setting_802_1x_set_phase2_private_key (s_8021x, + privkey, + privkey_password, + NM_SETTING_802_1X_CK_SCHEME_PATH, + &privkey_format, + error)) + goto done; + } else { + if (!nm_setting_802_1x_set_private_key (s_8021x, + privkey, + privkey_password, + NM_SETTING_802_1X_CK_SCHEME_PATH, + &privkey_format, error)) + goto done; + } + + /* Only set the client certificate if the private key is not PKCS#12 format, + * as NM (due to supplicant restrictions) requires. If the key was PKCS#12, + * then nm_setting_802_1x_set_private_key() already set the client certificate + * to the same value as the private key. + */ + if (privkey_format == NM_SETTING_802_1X_CK_FORMAT_RAW_KEY + || privkey_format == NM_SETTING_802_1X_CK_FORMAT_X509) { + client_cert = get_cert (ssid, phase2 ? "client_cert2" : "client_cert", basepath); + if (!client_cert) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Missing %s for EAP method '%s'.", + phase2 ? "IEEE_8021X_INNER_CLIENT_CERT" : + "IEEE_8021X_CLIENT_CERT", eap_method); + goto done; + } + + if (phase2) { + if (!nm_setting_802_1x_set_phase2_client_cert (s_8021x, + client_cert, + NM_SETTING_802_1X_CK_SCHEME_PATH, + NULL, + error)) + goto done; + } else { + if (!nm_setting_802_1x_set_client_cert (s_8021x, + client_cert, + NM_SETTING_802_1X_CK_SCHEME_PATH, + NULL, error)) + goto done; + } + } + + success = TRUE; + +done: + g_free (ca_cert); + g_free (client_cert); + g_free (privkey); + return success; +} + +static gboolean +eap_peap_reader (const char *eap_method, + const char *ssid, + NMSetting8021x *s_8021x, + gboolean phase2, + const char *basepath, + GError **error) +{ + char *ca_cert = NULL; + const char *inner_auth = NULL; + const char *peapver = NULL; + char **list = NULL, **iter, *lower; + gboolean success = FALSE; + + ca_cert = get_cert (ssid, "ca_cert", basepath); + if (ca_cert) { + if (!nm_setting_802_1x_set_ca_cert (s_8021x, + ca_cert, + NM_SETTING_802_1X_CK_SCHEME_PATH, + NULL, error)) + goto done; + } else { + nm_log_warn (LOGD_SETTINGS, " missing IEEE_8021X_CA_CERT for EAP method '%s'; this is insecure!", + eap_method); + } + + peapver = wpa_get_value (ssid, "phase1"); + /* peap version, default is automatic */ + if (peapver && strstr (peapver, "peapver")) { + if (strstr (peapver, "peapver=0")) + g_object_set (s_8021x, NM_SETTING_802_1X_PHASE1_PEAPVER, + "0", NULL); + else if (strstr (peapver, "peapver=1")) + g_object_set (s_8021x, NM_SETTING_802_1X_PHASE1_PEAPVER, + "1", NULL); + else { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Unknown IEEE_8021X_PEAP_VERSION value '%s'", + peapver); + goto done; + } + } + + /* peaplabel */ + if (peapver && strstr (peapver, "peaplabel=1")) + g_object_set (s_8021x, NM_SETTING_802_1X_PHASE1_PEAPLABEL, "1", + NULL); + + inner_auth = wpa_get_value (ssid, "phase2"); + if (!inner_auth) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Missing IEEE_8021X_INNER_AUTH_METHODS."); + goto done; + } + /* Handle options for the inner auth method */ + list = g_strsplit (inner_auth, " ", 0); + for (iter = list; iter && *iter; iter++) { + gchar *pos = NULL; + + if (!strlen (*iter)) + continue; + + if (!(pos = strstr (*iter, "MSCHAPV2")) + || !(pos = strstr (*iter, "MD5")) + || !(pos = strstr (*iter, "GTC"))) { + if (!eap_simple_reader (pos, ssid, s_8021x, TRUE, basepath, error)) + goto done; + } else if (!(pos = strstr (*iter, "TLS"))) { + if (!eap_tls_reader (pos, ssid, s_8021x, TRUE, basepath, error)) + goto done; + } else { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Unknown IEEE_8021X_INNER_AUTH_METHOD '%s'.", + *iter); + goto done; + } + + pos = strchr (*iter, '='); + if (pos && *pos) { + pos++; + lower = g_ascii_strdown (pos, -1); + g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_AUTH, lower, + NULL); + g_free (lower); + } else { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "No IEEE_8021X_INNER_AUTH_METHOD."); + goto done; + } + break; + } + + if (!nm_setting_802_1x_get_phase2_auth (s_8021x)) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "No valid IEEE_8021X_INNER_AUTH_METHODS found."); + goto done; + } + + success = TRUE; + +done: + g_free (ca_cert); + if (list) + g_strfreev (list); + return success; +} + +static gboolean +eap_ttls_reader (const char *eap_method, + const char *ssid, + NMSetting8021x *s_8021x, + gboolean phase2, + const char *basepath, + GError **error) +{ + gboolean success = FALSE; + const char *anon_ident = NULL; + char *ca_cert = NULL; + const char *tmp; + char **list = NULL, **iter, *inner_auth = NULL; + + /* ca cert */ + ca_cert = get_cert (ssid, "ca_cert", basepath); + if (ca_cert) { + if (!nm_setting_802_1x_set_ca_cert (s_8021x, + ca_cert, + NM_SETTING_802_1X_CK_SCHEME_PATH, + NULL, error)) + goto done; + } else { + nm_log_warn (LOGD_SETTINGS, " missing IEEE_8021X_CA_CERT for EAP method '%s'; this is insecure!", + eap_method); + } + + /* anonymous indentity for tls */ + anon_ident = wpa_get_value (ssid, "anonymous_identity"); + if (anon_ident && strlen (anon_ident)) + g_object_set (s_8021x, NM_SETTING_802_1X_ANONYMOUS_IDENTITY, + anon_ident, NULL); + + tmp = wpa_get_value (ssid, "phase2"); + if (!tmp) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Missing IEEE_8021X_INNER_AUTH_METHODS."); + goto done; + } + + /* Handle options for the inner auth method */ + inner_auth = g_ascii_strdown (tmp, -1); + list = g_strsplit (inner_auth, " ", 0); + for (iter = list; iter && *iter; iter++) { + gchar *pos = NULL; + + if (!strlen (*iter)) + continue; + if ((pos = strstr (*iter, "mschapv2")) != NULL + || (pos = strstr (*iter, "mschap")) != NULL + || (pos = strstr (*iter, "pap")) != NULL + || (pos = strstr (*iter, "chap")) != NULL) { + if (!eap_simple_reader (pos, ssid, s_8021x, TRUE, basepath, error)) + goto done; + g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_AUTH, + pos, NULL); + } else if ((pos = strstr (*iter, "tls")) != NULL) { + if (!eap_tls_reader (pos, ssid, s_8021x, TRUE, basepath, error)) + goto done; + g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_AUTHEAP, + "tls", NULL); + } else if ((pos = strstr (*iter, "mschapv2")) != NULL + || (pos = strstr (*iter, "md5")) != NULL) { + if (!eap_simple_reader (pos, ssid, s_8021x, TRUE, basepath, error)) { + nm_log_warn (LOGD_SETTINGS, "SIMPLE ERROR"); + goto done; + } + g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_AUTHEAP, + pos, NULL); + } else { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Unknown IEEE_8021X_INNER_AUTH_METHOD '%s'.", + *iter); + goto done; + } + break; + } + + success = TRUE; +done: + g_free (ca_cert); + if (list) + g_strfreev (list); + g_free (inner_auth); + return success; +} + +/* type is already decided by net_parser, this function is just used to + * doing tansformation*/ +static const gchar * +guess_connection_type (const char *conn_name) +{ + const gchar *type = ifnet_get_data (conn_name, "type"); + const gchar *ret_type = NULL; + + if (!g_strcmp0 (type, "ppp")) + ret_type = NM_SETTING_PPPOE_SETTING_NAME; + + if (!g_strcmp0 (type, "wireless")) + ret_type = NM_SETTING_WIRELESS_SETTING_NAME; + + if (!ret_type) + ret_type = NM_SETTING_WIRED_SETTING_NAME; + + nm_log_info (LOGD_SETTINGS, "guessed connection type (%s) = %s", conn_name, ret_type); + return ret_type; +} + +/* Reading mac address for setting connection option. + * Unmanaged device mac address is required by NetworkManager*/ +static gboolean +read_mac_address (const char *conn_name, const char **mac, GError **error) +{ + const char *value = ifnet_get_data (conn_name, "mac"); + + if (!value || !strlen (value)) + return TRUE; + + if (!nm_utils_hwaddr_valid (value, ETH_ALEN)) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "The MAC address '%s' was invalid.", value); + return FALSE; + } + + *mac = value; + return TRUE; +} + +static gboolean +make_wired_connection_setting (NMConnection *connection, + const char *conn_name, + GError **error) +{ + const char *mac = NULL; + NMSettingWired *s_wired = NULL; + const char *value = NULL; + + s_wired = NM_SETTING_WIRED (nm_setting_wired_new ()); + + /* mtu_xxx */ + value = ifnet_get_data (conn_name, "mtu"); + if (value) { + long int mtu; + + errno = 0; + mtu = strtol (value, NULL, 10); + if (errno || mtu < 0 || mtu > 65535) { + nm_log_warn (LOGD_SETTINGS, " invalid MTU '%s' for %s", value, conn_name); + } else + g_object_set (s_wired, NM_SETTING_WIRED_MTU, + (guint32) mtu, NULL); + } + + if (!read_mac_address (conn_name, &mac, error)) { + g_object_unref (s_wired); + return FALSE; + } + + if (mac) + g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, mac, NULL); + nm_connection_add_setting (connection, NM_SETTING (s_wired)); + + return TRUE; +} + +/* add NM_SETTING_IP_CONFIG_DHCP_HOSTNAME, + * NM_SETTING_IP_CONFIG_DHCP_CLIENT_ID in future*/ +static gboolean +make_ip4_setting (NMConnection *connection, + const char *conn_name, + GError **error) +{ + + NMSettingIPConfig *ip4_setting = + NM_SETTING_IP_CONFIG (nm_setting_ip4_config_new ()); + const char *value, *method = NULL; + gboolean is_static_block = is_static_ip4 (conn_name); + ip_block *iblock = NULL; + + /* set dhcp options (dhcp_xxx) */ + value = ifnet_get_data (conn_name, "dhcp"); + g_object_set (ip4_setting, NM_SETTING_IP_CONFIG_IGNORE_AUTO_DNS, value + && strstr (value, "nodns") ? TRUE : FALSE, + NM_SETTING_IP_CONFIG_IGNORE_AUTO_ROUTES, value + && strstr (value, "nogateway") ? TRUE : FALSE, NULL); + + if (!is_static_block) { + method = ifnet_get_data (conn_name, "config"); + if (!method){ + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Unknown config for %s", conn_name); + g_object_unref (ip4_setting); + return FALSE; + } + if (strstr (method, "dhcp")) + g_object_set (ip4_setting, + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP4_CONFIG_METHOD_AUTO, + NM_SETTING_IP_CONFIG_NEVER_DEFAULT, FALSE, NULL); + else if (strstr (method, "autoip")) { + g_object_set (ip4_setting, + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL, + NM_SETTING_IP_CONFIG_NEVER_DEFAULT, FALSE, NULL); + nm_connection_add_setting (connection, NM_SETTING (ip4_setting)); + return TRUE; + } else if (strstr (method, "shared")) { + g_object_set (ip4_setting, + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP4_CONFIG_METHOD_SHARED, + NM_SETTING_IP_CONFIG_NEVER_DEFAULT, FALSE, NULL); + nm_connection_add_setting (connection, NM_SETTING (ip4_setting)); + return TRUE; + } else { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Unknown config for %s", conn_name); + g_object_unref (ip4_setting); + return FALSE; + } + nm_log_info (LOGD_SETTINGS, "Using %s method for %s", method, conn_name); + }else { + iblock = convert_ip4_config_block (conn_name); + if (!iblock) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Ifnet plugin: can't aquire ip configuration for %s", + conn_name); + g_object_unref (ip4_setting); + return FALSE; + } + /************** add all ip settings to the connection**********/ + while (iblock) { + ip_block *current_iblock; + NMIPAddress *ip4_addr; + GError *local = NULL; + + ip4_addr = nm_ip_address_new (AF_INET, iblock->ip, iblock->prefix, &local); + if (iblock->next_hop) + g_object_set (ip4_setting, + NM_SETTING_IP_CONFIG_IGNORE_AUTO_ROUTES, + TRUE, NULL); + + if (ip4_addr) { + if (!nm_setting_ip_config_add_address (ip4_setting, ip4_addr)) + nm_log_warn (LOGD_SETTINGS, "ignoring duplicate IP4 address"); + nm_ip_address_unref (ip4_addr); + } else { + nm_log_warn (LOGD_SETTINGS, " ignoring invalid address entry: %s", local->message); + g_clear_error (&local); + } + + current_iblock = iblock; + iblock = iblock->next; + destroy_ip_block (current_iblock); + + } + g_object_set (ip4_setting, + NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_MANUAL, + NM_SETTING_IP_CONFIG_NEVER_DEFAULT, !has_default_ip4_route (conn_name), + NULL); + } + + /* add dhcp hostname and client id */ + if (!is_static_block && strstr (method, "dhcp")) { + gchar *dhcp_hostname, *client_id; + + get_dhcp_hostname_and_client_id (&dhcp_hostname, &client_id); + if (dhcp_hostname) { + g_object_set (ip4_setting, + NM_SETTING_IP_CONFIG_DHCP_HOSTNAME, + dhcp_hostname, NULL); + nm_log_info (LOGD_SETTINGS, "DHCP hostname: %s", dhcp_hostname); + g_free (dhcp_hostname); + } + if (client_id) { + g_object_set (ip4_setting, + NM_SETTING_IP4_CONFIG_DHCP_CLIENT_ID, + client_id, NULL); + nm_log_info (LOGD_SETTINGS, "DHCP client id: %s", client_id); + g_free (client_id); + } + } + + /* add all IPv4 dns servers, IPv6 servers will be ignored */ + set_ip4_dns_servers (ip4_setting, conn_name); + + /* DNS searches */ + value = ifnet_get_data (conn_name, "dns_search"); + if (value) { + gs_free char *stripped = g_strdup (value); + char **searches = NULL; + + strip_string (stripped, '"'); + + searches = g_strsplit (stripped, " ", 0); + if (searches) { + char **item; + + for (item = searches; *item; item++) { + if (strlen (*item)) { + if (!nm_setting_ip_config_add_dns_search (ip4_setting, *item)) + nm_log_warn (LOGD_SETTINGS, " duplicate DNS domain '%s'", *item); + } + } + g_strfreev (searches); + } + } + + /* static routes */ + iblock = convert_ip4_routes_block (conn_name); + while (iblock) { + ip_block *current_iblock = iblock; + const char *metric_str; + char *stripped; + gint64 metric; + NMIPRoute *route; + GError *local = NULL; + + if ((metric_str = ifnet_get_data (conn_name, "metric")) != NULL) { + metric = _nm_utils_ascii_str_to_int64 (metric_str, 10, 0, G_MAXUINT32, -1); + } else { + metric_str = ifnet_get_global_data ("metric"); + if (metric_str) { + stripped = g_strdup (metric_str); + strip_string (stripped, '"'); + metric = _nm_utils_ascii_str_to_int64 (metric_str, 10, 0, G_MAXUINT32, -1); + g_free (stripped); + } else + metric = -1; + } + + route = nm_ip_route_new (AF_INET, iblock->ip, iblock->prefix, iblock->next_hop, metric, &local); + if (route) { + if (nm_setting_ip_config_add_route (ip4_setting, route)) + nm_log_info (LOGD_SETTINGS, "new IP4 route:%s\n", iblock->ip); + else + nm_log_warn (LOGD_SETTINGS, "duplicate IP4 route"); + nm_ip_route_unref (route); + } else { + nm_log_warn (LOGD_SETTINGS, " ignoring invalid route entry: %s", local->message); + g_clear_error (&local); + } + + current_iblock = iblock; + iblock = iblock->next; + destroy_ip_block (current_iblock); + } + + /* Finally add setting to connection */ + nm_connection_add_setting (connection, NM_SETTING (ip4_setting)); + + return TRUE; +} + +static gboolean +make_ip6_setting (NMConnection *connection, + const char *conn_name, + GError **error) +{ + NMSettingIPConfig *s_ip6 = NULL; + gboolean is_static_block = is_static_ip6 (conn_name); + + // used to disable IPv6 + gboolean ipv6_enabled = FALSE; + gchar *method = NM_SETTING_IP6_CONFIG_METHOD_MANUAL; + const char *value; + ip_block *iblock; + gboolean never_default = !has_default_ip6_route (conn_name); + + s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new (); + + value = ifnet_get_data (conn_name, "enable_ipv6"); + if (value && is_true (value)) + ipv6_enabled = TRUE; + + //FIXME Handle other methods that NM supports in future + // Currently only Manual and DHCP are supported + if (!ipv6_enabled) { + g_object_set (s_ip6, + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP6_CONFIG_METHOD_IGNORE, NULL); + goto done; + } else if (!is_static_block) { + // config_eth* contains "dhcp6" + method = NM_SETTING_IP6_CONFIG_METHOD_AUTO; + never_default = FALSE; + } + // else if (!has_ip6_address(conn_name)) + // doesn't have "dhcp6" && doesn't have any ipv6 address + // method = NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL; + else + // doesn't have "dhcp6" && has at least one ipv6 address + method = NM_SETTING_IP6_CONFIG_METHOD_MANUAL; + nm_log_info (LOGD_SETTINGS, "IPv6 for %s enabled, using %s", conn_name, method); + + g_object_set (s_ip6, + NM_SETTING_IP_CONFIG_METHOD, method, + NM_SETTING_IP_CONFIG_IGNORE_AUTO_DNS, FALSE, + NM_SETTING_IP_CONFIG_IGNORE_AUTO_ROUTES, FALSE, + NM_SETTING_IP_CONFIG_NEVER_DEFAULT, never_default, NULL); + + /* Make manual settings */ + if (!strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_MANUAL)) { + ip_block *current_iblock; + + iblock = convert_ip6_config_block (conn_name); + if (!iblock) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Ifnet plugin: can't aquire ip6 configuration for %s", + conn_name); + goto error; + } + /* add all IPv6 addresses */ + while (iblock) { + NMIPAddress *ip6_addr; + GError *local = NULL; + + ip6_addr = nm_ip_address_new (AF_INET6, iblock->ip, iblock->prefix, &local); + if (ip6_addr) { + if (nm_setting_ip_config_add_address (s_ip6, ip6_addr)) { + nm_log_info (LOGD_SETTINGS, "ipv6 addresses count: %d", + nm_setting_ip_config_get_num_addresses (s_ip6)); + } else { + nm_log_warn (LOGD_SETTINGS, "ignoring duplicate IP6 address"); + } + nm_ip_address_unref (ip6_addr); + } else { + nm_log_warn (LOGD_SETTINGS, " ignoring invalid address entry: %s", local->message); + g_clear_error (&local); + } + + current_iblock = iblock; + iblock = iblock->next; + destroy_ip_block (current_iblock); + } + + } else if (!strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO)) { + /* - autoconf or DHCPv6 stuff goes here */ + } + // DNS Servers, set NM_SETTING_IP_CONFIG_IGNORE_AUTO_DNS TRUE here + set_ip6_dns_servers (s_ip6, conn_name); + + /* DNS searches ('DOMAIN' key) are read by make_ip4_setting() and included in NMSettingIPConfig */ + + // Add routes + iblock = convert_ip6_routes_block (conn_name); + if (iblock) + g_object_set (s_ip6, NM_SETTING_IP_CONFIG_IGNORE_AUTO_ROUTES, + TRUE, NULL); + /* Add all IPv6 routes */ + while (iblock) { + ip_block *current_iblock = iblock; + const char *metric_str; + char *stripped; + gint64 metric; + NMIPRoute *route; + GError *local = NULL; + + /* metric is not per routes configuration right now + * global metric is also supported (metric="x") */ + if ((metric_str = ifnet_get_data (conn_name, "metric")) != NULL) + metric = _nm_utils_ascii_str_to_int64 (metric_str, 10, 0, G_MAXUINT32, -1); + else { + metric_str = ifnet_get_global_data ("metric"); + if (metric_str) { + stripped = g_strdup (metric_str); + strip_string (stripped, '"'); + metric = _nm_utils_ascii_str_to_int64 (metric_str, 10, 0, G_MAXUINT32, -1); + g_free (stripped); + } else + metric = 1; + } + + route = nm_ip_route_new (AF_INET6, iblock->ip, iblock->prefix, iblock->next_hop, metric, &local); + if (route) { + if (nm_setting_ip_config_add_route (s_ip6, route)) + nm_log_info (LOGD_SETTINGS, " new IP6 route"); + else + nm_log_warn (LOGD_SETTINGS, " duplicate IP6 route"); + nm_ip_route_unref (route); + } else { + nm_log_warn (LOGD_SETTINGS, " ignoring invalid route entry: %s", local->message); + g_clear_error (&local); + } + + current_iblock = iblock; + iblock = iblock->next; + destroy_ip_block (current_iblock); + } + +done: + nm_connection_add_setting (connection, NM_SETTING (s_ip6)); + return TRUE; + +error: + g_object_unref (s_ip6); + nm_log_warn (LOGD_SETTINGS, " Ignore IPv6 for %s", conn_name); + return FALSE; +} + +static NMSetting * +make_wireless_connection_setting (const char *conn_name, + NMSetting8021x **s_8021x, + GError **error) +{ + const char *mac = NULL; + NMSettingWireless *wireless_setting = NULL; + gboolean adhoc = FALSE; + const char *value; + const char *type; + + /* PPP over WIFI is not supported yet */ + g_return_val_if_fail (conn_name != NULL + && strcmp (ifnet_get_data (conn_name, "type"), + "ppp") != 0, NULL); + type = ifnet_get_data (conn_name, "type"); + if (strcmp (type, "ppp") == 0) { + nm_log_warn (LOGD_SETTINGS, "PPP over WIFI is not supported yet"); + return NULL; + } + + wireless_setting = NM_SETTING_WIRELESS (nm_setting_wireless_new ()); + if (read_mac_address (conn_name, &mac, error)) { + if (mac) { + g_object_set (wireless_setting, + NM_SETTING_WIRELESS_MAC_ADDRESS, mac, + NULL); + } + } else { + g_object_unref (wireless_setting); + return NULL; + } + + /* handle ssid (hex and ascii) */ + if (conn_name) { + GBytes *bytes; + gsize ssid_len = 0, value_len = strlen (conn_name); + + ssid_len = value_len; + if ((value_len > 2) && (g_str_has_prefix (conn_name, "0x"))) { + /* Hex representation */ + if (value_len % 2) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Invalid SSID '%s' size (looks like hex but length not multiple of 2)", + conn_name); + goto error; + } + + bytes = nm_utils_hexstr2bin (conn_name); + if (!bytes) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Invalid SSID '%s' (looks like hex SSID but isn't)", + conn_name); + goto error; + } + } else + bytes = g_bytes_new (conn_name, value_len); + + ssid_len = g_bytes_get_size (bytes); + if (ssid_len > 32 || ssid_len == 0) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Invalid SSID '%s' (size %zu not between 1 and 32 inclusive)", + conn_name, ssid_len); + goto error; + } + + g_object_set (wireless_setting, NM_SETTING_WIRELESS_SSID, bytes, NULL); + g_bytes_unref (bytes); + } else { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Missing SSID"); + goto error; + } + + /* mode=0: infrastructure + * mode=1: adhoc */ + value = wpa_get_value (conn_name, "mode"); + if (value) + adhoc = strcmp (value, "1") == 0 ? TRUE : FALSE; + + if (exist_ssid (conn_name)) { + const char *mode = adhoc ? "adhoc" : "infrastructure"; + + g_object_set (wireless_setting, NM_SETTING_WIRELESS_MODE, mode, + NULL); + nm_log_info (LOGD_SETTINGS, "Using mode: %s", mode); + } + + /* BSSID setting */ + value = wpa_get_value (conn_name, "bssid"); + if (value) { + if (!nm_utils_hwaddr_valid (value, ETH_ALEN)) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Invalid BSSID '%s'", value); + goto error; + } + + g_object_set (wireless_setting, NM_SETTING_WIRELESS_BSSID, + value, NULL); + + } + + /* mtu_ssid="xx" */ + value = ifnet_get_data (conn_name, "mtu"); + if (value) { + long int mtu; + + errno = 0; + mtu = strtol (value, NULL, 10); + if (errno || mtu < 0 || mtu > 50000) { + nm_log_warn (LOGD_SETTINGS, " invalid MTU '%s' for %s", value, conn_name); + } else + g_object_set (wireless_setting, NM_SETTING_WIRELESS_MTU, + (guint32) mtu, NULL); + + } + + nm_log_info (LOGD_SETTINGS, "wireless_setting added for %s", conn_name); + return NM_SETTING (wireless_setting); +error: + if (wireless_setting) + g_object_unref (wireless_setting); + return NULL; + +} + +static NMSettingWirelessSecurity * +make_leap_setting (const char *ssid, GError **error) +{ + NMSettingWirelessSecurity *wsec; + const char *value; + + wsec = + NM_SETTING_WIRELESS_SECURITY (nm_setting_wireless_security_new ()); + + value = wpa_get_value (ssid, "password"); + if (value && strlen (value)) + g_object_set (wsec, NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD, + value, NULL); + + value = wpa_get_value (ssid, "identity"); + if (!value || !strlen (value)) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Missing LEAP identity"); + goto error; + } + g_object_set (wsec, NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME, value, + NULL); + + g_object_set (wsec, + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "ieee8021x", + NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "leap", NULL); + + return wsec; +error: + if (wsec) + g_object_unref (wsec); + return NULL; +} + +static gboolean +add_one_wep_key (const char *ssid, + const char *key, + int key_idx, + NMSettingWirelessSecurity *s_wsec, + GError **error) +{ + const char *value; + char *converted = NULL; + gboolean success = FALSE; + + g_return_val_if_fail (ssid != NULL, FALSE); + g_return_val_if_fail (key != NULL, FALSE); + g_return_val_if_fail (key_idx >= 0 && key_idx <= 3, FALSE); + g_return_val_if_fail (s_wsec != NULL, FALSE); + + value = wpa_get_value (ssid, key); + if (!value) + return TRUE; + + /* Validate keys */ + if (strlen (value) == 10 || strlen (value) == 26) { + /* Hexadecimal WEP key */ + if (!is_hex (value)) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Invalid hexadecimal WEP key."); + goto out; + } + converted = g_strdup (value); + } else if (value[0] == '"' + && (strlen (value) == 7 || strlen (value) == 15)) { + /* ASCII passphrase */ + char *tmp = g_strdup (value); + char *p = strip_string (tmp, '"'); + + if (!is_ascii (p)) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Invalid ASCII WEP passphrase."); + g_free (tmp); + goto out; + + } + + converted = nm_utils_bin2hexstr (tmp, strlen (tmp), -1); + g_free (tmp); + } else { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Invalid WEP key length. Key: %s", value); + goto out; + } + + if (converted) { + nm_setting_wireless_security_set_wep_key (s_wsec, key_idx, converted); + g_free (converted); + success = TRUE; + } + +out: + return success; +} + +static gboolean +add_wep_keys (const char *ssid, + NMSettingWirelessSecurity *s_wsec, + GError **error) +{ + if (!add_one_wep_key (ssid, "wep_key0", 0, s_wsec, error)) + return FALSE; + if (!add_one_wep_key (ssid, "wep_key1", 1, s_wsec, error)) + return FALSE; + if (!add_one_wep_key (ssid, "wep_key2", 2, s_wsec, error)) + return FALSE; + if (!add_one_wep_key (ssid, "wep_key3", 3, s_wsec, error)) + return FALSE; + return TRUE; + +} + +static NMSettingWirelessSecurity * +make_wep_setting (const char *ssid, GError **error) +{ + const char *auth_alg, *value; + int default_key_idx = 0; + NMSettingWirelessSecurity *s_wireless_sec; + + s_wireless_sec = + NM_SETTING_WIRELESS_SECURITY (nm_setting_wireless_security_new ()); + g_object_set (s_wireless_sec, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, + "none", NULL); + + /* default key index */ + value = wpa_get_value (ssid, "wep_tx_keyidx"); + if (value) { + default_key_idx = atoi (value); + if (default_key_idx >= 0 && default_key_idx <= 3) { + g_object_set (s_wireless_sec, + NM_SETTING_WIRELESS_SECURITY_WEP_TX_KEYIDX, + default_key_idx, NULL); + nm_log_info (LOGD_SETTINGS, "Default key index: %d", default_key_idx); + } else { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Invalid default WEP key '%s'", value); + goto error; + } + } + + if (!add_wep_keys (ssid, s_wireless_sec, error)) + goto error; + + /* If there's a default key, ensure that key exists */ + if ((default_key_idx == 1) + && !nm_setting_wireless_security_get_wep_key (s_wireless_sec, 1)) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Default WEP key index was 2, but no valid KEY2 exists."); + goto error; + } else if ((default_key_idx == 2) + && !nm_setting_wireless_security_get_wep_key (s_wireless_sec, + 2)) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Default WEP key index was 3, but no valid KEY3 exists."); + goto error; + } else if ((default_key_idx == 3) + && !nm_setting_wireless_security_get_wep_key (s_wireless_sec, + 3)) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Default WEP key index was 4, but no valid KEY4 exists."); + goto error; + } + + /* authentication algorithms */ + auth_alg = wpa_get_value (ssid, "auth_alg"); + if (auth_alg) { + if (strcmp (auth_alg, "OPEN") == 0) { + g_object_set (s_wireless_sec, + NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, + "open", NULL); + nm_log_info (LOGD_SETTINGS, "WEP: Use open system authentication"); + } else if (strcmp (auth_alg, "SHARED") == 0) { + g_object_set (s_wireless_sec, + NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, + "shared", NULL); + nm_log_info (LOGD_SETTINGS, "WEP: Use shared system authentication"); + } else { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Invalid WEP authentication algorithm '%s'", + auth_alg); + goto error; + } + + } + + if (!nm_setting_wireless_security_get_wep_key (s_wireless_sec, 0) + && !nm_setting_wireless_security_get_wep_key (s_wireless_sec, 1) + && !nm_setting_wireless_security_get_wep_key (s_wireless_sec, 2) + && !nm_setting_wireless_security_get_wep_key (s_wireless_sec, 3) + && !nm_setting_wireless_security_get_wep_tx_keyidx (s_wireless_sec)) { + if (auth_alg && !strcmp (auth_alg, "shared")) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "WEP Shared Key authentication is invalid for " + "unencrypted connections."); + goto error; + } + /* Unencrypted */ + g_object_unref (s_wireless_sec); + s_wireless_sec = NULL; + } + return s_wireless_sec; + +error: + if (s_wireless_sec) + g_object_unref (s_wireless_sec); + return NULL; +} + +static char * +parse_wpa_psk (const char *psk, GError **error) +{ + char *hashed = NULL; + gboolean quoted = FALSE; + + if (!psk) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Missing WPA_PSK for WPA-PSK key management"); + return NULL; + } + + /* Passphrase must be between 10 and 66 characters in length because WPA + * hex keys are exactly 64 characters (no quoting), and WPA passphrases + * are between 8 and 63 characters (inclusive), plus optional quoting if + * the passphrase contains spaces. + */ + + if (psk[0] == '"' && psk[strlen (psk) - 1] == '"') + quoted = TRUE; + if (!quoted && (strlen (psk) == 64)) { + /* Verify the hex PSK; 64 digits */ + if (!is_hex (psk)) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Invalid WPA_PSK (contains non-hexadecimal characters)"); + goto out; + } + hashed = g_strdup (psk); + } else { + char *stripped = g_strdup (psk); + + strip_string (stripped, '"'); + + /* Length check */ + if (strlen (stripped) < 8 || strlen (stripped) > 63) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Invalid WPA_PSK (passphrases must be between " + "8 and 63 characters long (inclusive))"); + g_free (stripped); + goto out; + } + + hashed = g_strdup (stripped); + g_free (stripped); + } + + if (!hashed) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Invalid WPA_PSK (doesn't look like a passphrase or hex key)"); + goto out; + } + +out: + return hashed; +} + +static gboolean +fill_wpa_ciphers (const char *ssid, + NMSettingWirelessSecurity *wsec, + gboolean group, + gboolean adhoc) +{ + const char *value; + char **list = NULL, **iter; + int i = 0; + + value = wpa_get_value (ssid, group ? "group" : "pairwise"); + if (!value) + return TRUE; + + list = g_strsplit_set (value, " ", 0); + for (iter = list; iter && *iter; iter++, i++) { + /* Ad-Hoc configurations cannot have pairwise ciphers, and can only + * have one group cipher. Ignore any additional group ciphers and + * any pairwise ciphers specified. + */ + if (adhoc) { + if (group && (i > 0)) { + nm_log_warn (LOGD_SETTINGS, " ignoring group cipher '%s' (only one group cipher allowed in Ad-Hoc mode)", + *iter); + continue; + } else if (!group) { + nm_log_warn (LOGD_SETTINGS, " ignoring pairwise cipher '%s' (pairwise not used in Ad-Hoc mode)", + *iter); + continue; + } + } + + if (!strcmp (*iter, "CCMP")) { + if (group) + nm_setting_wireless_security_add_group (wsec, + "ccmp"); + else + nm_setting_wireless_security_add_pairwise (wsec, + "ccmp"); + } else if (!strcmp (*iter, "TKIP")) { + if (group) + nm_setting_wireless_security_add_group (wsec, + "tkip"); + else + nm_setting_wireless_security_add_pairwise (wsec, + "tkip"); + } else if (group && !strcmp (*iter, "WEP104")) + nm_setting_wireless_security_add_group (wsec, "wep104"); + else if (group && !strcmp (*iter, "WEP40")) + nm_setting_wireless_security_add_group (wsec, "wep40"); + else { + nm_log_warn (LOGD_SETTINGS, " ignoring invalid %s cipher '%s'", + group ? "CIPHER_GROUP" : "CIPHER_PAIRWISE", + *iter); + } + } + + if (list) + g_strfreev (list); + return TRUE; +} + +static NMSetting8021x * +fill_8021x (const char *ssid, + const char *key_mgmt, + gboolean wifi, + const char *basepath, + GError **error) +{ + NMSetting8021x *s_8021x; + const char *value; + char **list, **iter; + + value = wpa_get_value (ssid, "eap"); + if (!value) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Missing IEEE_8021X_EAP_METHODS for key management '%s'", + key_mgmt); + return NULL; + } + + list = g_strsplit (value, " ", 0); + + s_8021x = (NMSetting8021x *) nm_setting_802_1x_new (); + /* Validate and handle each EAP method */ + for (iter = list; iter && *iter; iter++) { + EAPReader *eap = &eap_readers[0]; + gboolean found = FALSE; + char *lower = NULL; + + lower = g_ascii_strdown (*iter, -1); + while (eap->method) { + if (strcmp (eap->method, lower)) + goto next; + + /* Some EAP methods don't provide keying material, thus they + * cannot be used with WiFi unless they are an inner method + * used with TTLS or PEAP or whatever. + */ + if (wifi && eap->wifi_phase2_only) { + nm_log_warn (LOGD_SETTINGS, " ignored invalid IEEE_8021X_EAP_METHOD '%s'; not allowed for wifi.", + lower); + goto next; + } + + /* Parse EAP method specific options */ + if (!(*eap->reader) (lower, ssid, s_8021x, FALSE, basepath, error)) { + g_free (lower); + goto error; + } + nm_setting_802_1x_add_eap_method (s_8021x, lower); + found = TRUE; + break; + + next: + eap++; + } + + if (!found) { + nm_log_warn (LOGD_SETTINGS, " ignored unknown IEEE_8021X_EAP_METHOD '%s'.", lower); + } + g_free (lower); + } + g_strfreev (list); + + if (nm_setting_802_1x_get_num_eap_methods (s_8021x) == 0) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "No valid EAP methods found in IEEE_8021X_EAP_METHODS."); + goto error; + } + + return s_8021x; + +error: + g_object_unref (s_8021x); + return NULL; +} + +static NMSettingWirelessSecurity * +make_wpa_setting (const char *ssid, + const char *basepath, + NMSetting8021x **s_8021x, + GError **error) +{ + NMSettingWirelessSecurity *wsec; + const char *value; + char *lower; + gboolean adhoc = FALSE; + + if (!exist_ssid (ssid)) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "No security info found for ssid: %s", ssid); + return NULL; + } + + wsec = NM_SETTING_WIRELESS_SECURITY (nm_setting_wireless_security_new ()); + + /* mode=1: adhoc + * mode=0: infrastructure */ + value = wpa_get_value (ssid, "mode"); + if (value) + adhoc = strcmp (value, "1") == 0 ? TRUE : FALSE; + + /* Pairwise and Group ciphers */ + fill_wpa_ciphers (ssid, wsec, FALSE, adhoc); + fill_wpa_ciphers (ssid, wsec, TRUE, adhoc); + + /* WPA and/or RSN */ + if (adhoc) { + /* Ad-Hoc mode only supports WPA proto for now */ + nm_setting_wireless_security_add_proto (wsec, "wpa"); + } else { + nm_setting_wireless_security_add_proto (wsec, "wpa"); + nm_setting_wireless_security_add_proto (wsec, "rsn"); + + } + + value = wpa_get_value (ssid, "key_mgmt"); + if (!strcmp (value, "WPA-PSK")) { + char *psk = parse_wpa_psk (wpa_get_value (ssid, "psk"), error); + + if (!psk) + goto error; + g_object_set (wsec, NM_SETTING_WIRELESS_SECURITY_PSK, psk, + NULL); + g_free (psk); + + if (adhoc) + g_object_set (wsec, + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, + "wpa-none", NULL); + else + g_object_set (wsec, + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, + "wpa-psk", NULL); + } else if (!strcmp (value, "WPA-EAP") || !strcmp (value, "IEEE8021X")) { + if (adhoc) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Ad-Hoc mode cannot be used with KEY_MGMT type '%s'", + value); + goto error; + } + *s_8021x = fill_8021x (ssid, value, TRUE, basepath, error); + if (!*s_8021x) + goto error; + + lower = g_ascii_strdown (value, -1); + g_object_set (wsec, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, + lower, NULL); + g_free (lower); + } else { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Unknown wireless KEY_MGMT type '%s'", value); + goto error; + } + return wsec; +error: + if (wsec) + g_object_unref (wsec); + return NULL; +} + +static NMSettingWirelessSecurity * +make_wireless_security_setting (const char *conn_name, + const char *basepath, + NMSetting8021x **s_8021x, + GError ** error) +{ + NMSettingWirelessSecurity *wsec = NULL; + const char *ssid; + gboolean adhoc = FALSE; + const char *value; + + g_return_val_if_fail (conn_name != NULL + && strcmp (ifnet_get_data (conn_name, "type"), + "ppp") != 0, NULL); + nm_log_info (LOGD_SETTINGS, "updating wireless security settings (%s).", conn_name); + + ssid = conn_name; + value = wpa_get_value (ssid, "mode"); + if (value) + adhoc = strcmp (value, "1") == 0 ? TRUE : FALSE; + + value = wpa_get_value (ssid, "key_mgmt"); + if (!adhoc && g_strcmp0 (value, "IEEE8021X") == 0) { + value = wpa_get_value (ssid, "eap"); + if (value && strcasecmp (value, "LEAP") == 0) { + wsec = make_leap_setting (ssid, error); + if (wsec == NULL) + goto error; + } + } else if (g_strcmp0 (value, "WPA-PSK") == 0 || g_strcmp0 (value, "WPA-EAP") == 0) { + wsec = make_wpa_setting (ssid, basepath, s_8021x, error); + if (wsec == NULL) + goto error; + } + if (!wsec) { + wsec = make_wep_setting (ssid, error); + if (wsec == NULL) + goto error; + } + return wsec; + +error: + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Can't handle security information for ssid: %s", + conn_name); + return NULL; +} + +/* Currently only support username and password */ +static gboolean +make_pppoe_connection_setting (NMConnection *connection, + const char *conn_name, + GError **error) +{ + NMSettingPppoe *s_pppoe; + NMSettingPpp *s_ppp; + const char *value; + + s_pppoe = NM_SETTING_PPPOE (nm_setting_pppoe_new ()); + + /* username */ + value = ifnet_get_data (conn_name, "username"); + if (!value) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "ppp requires at lease a username"); + return FALSE; + } + g_object_set (s_pppoe, NM_SETTING_PPPOE_USERNAME, value, NULL); + + /* password */ + value = ifnet_get_data (conn_name, "password"); + if (!value) { + value = ""; + } + + g_object_set (s_pppoe, NM_SETTING_PPPOE_PASSWORD, value, NULL); + nm_connection_add_setting (connection, NM_SETTING (s_pppoe)); + + /* PPP setting */ + s_ppp = (NMSettingPpp *) nm_setting_ppp_new (); + nm_connection_add_setting (connection, NM_SETTING (s_ppp)); + + return TRUE; +} + +NMConnection * +ifnet_update_connection_from_config_block (const char *conn_name, + const char *basepath, + GError **error) +{ + const gchar *type = NULL; + NMConnection *connection = NULL; + NMSettingConnection *setting = NULL; + NMSetting8021x *s_8021x = NULL; + NMSettingWirelessSecurity *wsec = NULL; + gboolean auto_conn = TRUE; + const char *value = NULL; + gchar *id, *uuid; + gboolean success = FALSE; + + connection = nm_simple_connection_new (); + setting = nm_connection_get_setting_connection (connection); + if (!setting) { + setting = NM_SETTING_CONNECTION (nm_setting_connection_new ()); + g_assert (setting); + nm_connection_add_setting (connection, NM_SETTING (setting)); + } + + type = guess_connection_type (conn_name); + value = ifnet_get_data (conn_name, "auto"); + if (value && !strcmp (value, "false")) + auto_conn = FALSE; + + /* Try to read UUID from the ifnet block, otherwise generate UUID from + * the connection ID. + */ + id = connection_id_from_ifnet_name (conn_name); + uuid = g_strdup (ifnet_get_data (conn_name, "uuid")); + if (!uuid) + uuid = nm_utils_uuid_generate_from_string (id, -1, NM_UTILS_UUID_TYPE_LEGACY, NULL); + + g_object_set (setting, + NM_SETTING_CONNECTION_TYPE, type, + NM_SETTING_CONNECTION_ID, id, + NM_SETTING_CONNECTION_UUID, uuid, + NM_SETTING_CONNECTION_INTERFACE_NAME, conn_name, + NM_SETTING_CONNECTION_READ_ONLY, FALSE, + NM_SETTING_CONNECTION_AUTOCONNECT, auto_conn, + NULL); + nm_log_info (LOGD_SETTINGS, "name:%s, id:%s, uuid: %s", conn_name, id, uuid); + g_free (id); + g_free (uuid); + + if (!strcmp (NM_SETTING_WIRED_SETTING_NAME, type) + || !strcmp (NM_SETTING_PPPOE_SETTING_NAME, type)) { + /* wired setting */ + if (!make_wired_connection_setting (connection, conn_name, error)) + goto error; + + /* pppoe setting */ + if (!strcmp (NM_SETTING_PPPOE_SETTING_NAME, type)) { + if (!make_pppoe_connection_setting (connection, conn_name, error)) + goto error; + } + } else if (!strcmp (NM_SETTING_WIRELESS_SETTING_NAME, type)) { + /* wireless setting */ + NMSetting *wireless_setting; + + wireless_setting = make_wireless_connection_setting (conn_name, &s_8021x, error); + if (!wireless_setting) + goto error; + nm_connection_add_setting (connection, wireless_setting); + + /* wireless security setting */ + if (wpa_get_value (conn_name, "ssid")) { + wsec = make_wireless_security_setting (conn_name, basepath, &s_8021x, error); + if (!wsec) + goto error; + nm_connection_add_setting (connection, NM_SETTING (wsec)); + if (s_8021x) + nm_connection_add_setting (connection, NM_SETTING (s_8021x)); + } + } else + goto error; + + /* IPv4 setting */ + if (!make_ip4_setting (connection, conn_name, error)) + goto error; + + /* IPv6 setting */ + if (!make_ip6_setting (connection, conn_name, error)) + goto error; + + if (nm_connection_verify (connection, error)) { + nm_log_info (LOGD_SETTINGS, "Connection verified %s:%d", conn_name, success); + } else { + goto error; + } + + return connection; +error: + g_object_unref (connection); + return NULL; +} + +typedef struct Setting8021xSchemeVtable { + const NMSetting8021xSchemeVtable *vtable; + const char *ifnet_key; +} Setting8021xSchemeVtable; + +static const Setting8021xSchemeVtable setting_8021x_scheme_vtable[] = { + [NM_SETTING_802_1X_SCHEME_TYPE_CA_CERT] = { + .vtable = &nm_setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_CA_CERT], + .ifnet_key = "ca_cert", + }, + [NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CA_CERT] = { + .vtable = &nm_setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CA_CERT], + .ifnet_key = "ca_cert2", + }, + [NM_SETTING_802_1X_SCHEME_TYPE_CLIENT_CERT] = { + .vtable = &nm_setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_CLIENT_CERT], + .ifnet_key = "client_cert", + }, + [NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CLIENT_CERT] = { + .vtable = &nm_setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CLIENT_CERT], + .ifnet_key = "client_cert2", + }, + [NM_SETTING_802_1X_SCHEME_TYPE_PRIVATE_KEY] = { + .vtable = &nm_setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PRIVATE_KEY], + .ifnet_key = "private_key", + }, + [NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_PRIVATE_KEY] = { + .vtable = &nm_setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_PRIVATE_KEY], + .ifnet_key = "private_key2", + }, +}; + +static gboolean +write_object (NMSetting8021x *s_8021x, + const char *conn_name, + GBytes *override_data, + const Setting8021xSchemeVtable *objtype, + GError **error) +{ + NMSetting8021xCKScheme scheme; + const char *path = NULL; + GBytes *blob = NULL; + + g_return_val_if_fail (conn_name != NULL, FALSE); + g_return_val_if_fail (objtype != NULL, FALSE); + if (override_data) + /* if given explicit data to save, always use that instead of asking + * the setting what to do. + */ + blob = override_data; + else { + scheme = (*(objtype->vtable->scheme_func)) (s_8021x); + switch (scheme) { + case NM_SETTING_802_1X_CK_SCHEME_BLOB: + blob = (*(objtype->vtable->blob_func)) (s_8021x); + break; + case NM_SETTING_802_1X_CK_SCHEME_PATH: + path = (*(objtype->vtable->path_func)) (s_8021x); + break; + default: + break; + } + } + + /* If the object path was specified, prefer that over any raw cert data that + * may have been sent. + */ + if (path) { + wpa_set_data (conn_name, (gchar *) objtype->ifnet_key, + (gchar *) path); + return TRUE; + } + + /* does not support writing encryption data now */ + if (blob) + nm_log_warn (LOGD_SETTINGS, " Currently we do not support cert writing."); + + return TRUE; +} + +static gboolean +write_8021x_certs (NMSetting8021x *s_8021x, + gboolean phase2, + const char *conn_name, + GError **error) +{ + char *password = NULL; + const Setting8021xSchemeVtable *otype = NULL; + gboolean is_pkcs12 = FALSE, success = FALSE; + GBytes *blob = NULL; + GBytes *enc_key = NULL; + gchar *generated_pw = NULL; + + /* CA certificate */ + otype = phase2 + ? &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CA_CERT] + : &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_CA_CERT]; + + if (!write_object (s_8021x, conn_name, NULL, otype, error)) + return FALSE; + + /* Private key */ + if (phase2) { + if (nm_setting_802_1x_get_phase2_private_key_scheme (s_8021x) != + NM_SETTING_802_1X_CK_SCHEME_UNKNOWN) { + if (nm_setting_802_1x_get_phase2_private_key_format + (s_8021x) == NM_SETTING_802_1X_CK_FORMAT_PKCS12) + is_pkcs12 = TRUE; + } + password = (char *) + nm_setting_802_1x_get_phase2_private_key_password (s_8021x); + } else { + if (nm_setting_802_1x_get_private_key_scheme (s_8021x) != + NM_SETTING_802_1X_CK_SCHEME_UNKNOWN) { + if (nm_setting_802_1x_get_private_key_format (s_8021x) + == NM_SETTING_802_1X_CK_FORMAT_PKCS12) + is_pkcs12 = TRUE; + } + password = (char *) + nm_setting_802_1x_get_private_key_password (s_8021x); + } + + otype = phase2 + ? &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_PRIVATE_KEY] + : &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PRIVATE_KEY]; + + if ((*(otype->vtable->scheme_func)) (s_8021x) == + NM_SETTING_802_1X_CK_SCHEME_BLOB) + blob = (*(otype->vtable->blob_func)) (s_8021x); + + /* Only do the private key re-encrypt dance if we got the raw key data, which + * by definition will be unencrypted. If we're given a direct path to the + * private key file, it'll be encrypted, so we don't need to re-encrypt. + */ + if (blob && !is_pkcs12) { + GByteArray *tmp_enc_key; + + /* Encrypt the unencrypted private key with the fake password */ + tmp_enc_key = + nm_utils_rsa_key_encrypt (g_bytes_get_data (blob, NULL), g_bytes_get_size (blob), + password, &generated_pw, error); + if (!tmp_enc_key) + goto out; + + enc_key = g_byte_array_free_to_bytes (tmp_enc_key); + + if (generated_pw) + password = generated_pw; + } + + /* Save the private key */ + if (!write_object + (s_8021x, conn_name, enc_key ? enc_key : blob, otype, error)) + goto out; + + if (phase2) + wpa_set_data (conn_name, "private_key2_passwd", password); + else + wpa_set_data (conn_name, "private_key_passwd", password); + + /* Client certificate */ + if (is_pkcs12) { + wpa_set_data (conn_name, + phase2 ? "client_cert2" : "client_cert", NULL); + } else { + otype = phase2 + ? &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CLIENT_CERT] + : &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_CLIENT_CERT]; + + /* Save the client certificate */ + if (!write_object (s_8021x, conn_name, NULL, otype, error)) + goto out; + } + + success = TRUE; +out: + if (generated_pw) { + memset (generated_pw, 0, strlen (generated_pw)); + g_free (generated_pw); + } + if (enc_key) { + memset ((gpointer) g_bytes_get_data (enc_key, NULL), 0, g_bytes_get_size (enc_key)); + g_bytes_unref (enc_key); + } + return success; +} + +static gboolean +write_8021x_setting (NMConnection *connection, + const char *conn_name, + gboolean wired, + GError **error) +{ + NMSetting8021x *s_8021x; + const char *value; + char *tmp = NULL; + gboolean success = FALSE; + GString *phase2_auth; + GString *phase1; + + s_8021x = nm_connection_get_setting_802_1x (connection); + if (!s_8021x) { + return TRUE; + } + + nm_log_info (LOGD_SETTINGS, "Adding 8021x setting for %s", conn_name); + + /* If wired, write KEY_MGMT */ + if (wired) + wpa_set_data (conn_name, "key_mgmt", "IEEE8021X"); + + /* EAP method */ + if (nm_setting_802_1x_get_num_eap_methods (s_8021x)) { + value = nm_setting_802_1x_get_eap_method (s_8021x, 0); + if (value) + tmp = g_ascii_strup (value, -1); + } + wpa_set_data (conn_name, "eap", tmp ? tmp : NULL); + g_free (tmp); + + wpa_set_data (conn_name, "identity", + (gchar *) nm_setting_802_1x_get_identity (s_8021x)); + + wpa_set_data (conn_name, "anonymous_identity", (gchar *) + nm_setting_802_1x_get_anonymous_identity (s_8021x)); + + wpa_set_data (conn_name, "password", + (gchar *) nm_setting_802_1x_get_password (s_8021x)); + + phase1 = g_string_new (NULL); + + /* PEAP version */ + wpa_set_data (conn_name, "phase1", NULL); + value = nm_setting_802_1x_get_phase1_peapver (s_8021x); + if (value && (!strcmp (value, "0") || !strcmp (value, "1"))) + g_string_append_printf (phase1, "peapver=%s ", value); + + /* PEAP label */ + value = nm_setting_802_1x_get_phase1_peaplabel (s_8021x); + if (value && !strcmp (value, "1")) + g_string_append_printf (phase1, "peaplabel=%s ", value); + if (phase1->len) { + tmp = g_strstrip (g_strdup (phase1->str)); + wpa_set_data (conn_name, "phase1", tmp); + g_free (tmp); + } + + /* Phase2 auth methods */ + wpa_set_data (conn_name, "phase2", NULL); + phase2_auth = g_string_new (NULL); + + value = nm_setting_802_1x_get_phase2_auth (s_8021x); + if (value) { + tmp = g_ascii_strup (value, -1); + g_string_append_printf (phase2_auth, "auth=%s ", tmp); + g_free (tmp); + } + + /* Phase2 auth heap */ + value = nm_setting_802_1x_get_phase2_autheap (s_8021x); + if (value) { + tmp = g_ascii_strup (value, -1); + g_string_append_printf (phase2_auth, "autheap=%s ", tmp); + g_free (tmp); + } + tmp = g_strstrip (g_strdup (phase2_auth->str)); + wpa_set_data (conn_name, "phase2", phase2_auth->len ? tmp : NULL); + g_free (tmp); + + g_string_free (phase2_auth, TRUE); + g_string_free (phase1, TRUE); + + success = write_8021x_certs (s_8021x, FALSE, conn_name, error); + if (success) { + /* phase2/inner certs */ + success = write_8021x_certs (s_8021x, TRUE, conn_name, error); + } + + return success; +} + +static gboolean +write_wireless_security_setting (NMConnection * connection, + gchar * conn_name, + gboolean adhoc, + gboolean * no_8021x, GError ** error) +{ + NMSettingWirelessSecurity *s_wsec; + const char *key_mgmt, *auth_alg, *key, *cipher, *psk; + gboolean wep = FALSE, wpa = FALSE; + char *tmp; + guint32 i, num; + GString *str; + + s_wsec = nm_connection_get_setting_wireless_security (connection); + if (!s_wsec) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Missing '%s' setting", + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME); + return FALSE; + } + + key_mgmt = nm_setting_wireless_security_get_key_mgmt (s_wsec); + g_assert (key_mgmt); + + auth_alg = nm_setting_wireless_security_get_auth_alg (s_wsec); + + if (!strcmp (key_mgmt, "none")) { + wpa_set_data (conn_name, "key_mgmt", "NONE"); + wep = TRUE; + *no_8021x = TRUE; + } else if (!strcmp (key_mgmt, "wpa-none") + || !strcmp (key_mgmt, "wpa-psk")) { + wpa_set_data (conn_name, "key_mgmt", "WPA-PSK"); + wpa = TRUE; + *no_8021x = TRUE; + } else if (!strcmp (key_mgmt, "ieee8021x")) { + wpa_set_data (conn_name, "key_mgmt", "IEEE8021X"); + } else if (!strcmp (key_mgmt, "wpa-eap")) { + wpa_set_data (conn_name, "key_mgmt", "WPA-EAP"); + wpa = TRUE; + } else + nm_log_warn (LOGD_SETTINGS, "Unknown key_mgmt: %s", key_mgmt); + + if (auth_alg) { + if (!strcmp (auth_alg, "shared")) + wpa_set_data (conn_name, "auth_alg", "SHARED"); + else if (!strcmp (auth_alg, "open")) + wpa_set_data (conn_name, "auth_alg", "OPEN"); + else if (!strcmp (auth_alg, "leap")) { + wpa_set_data (conn_name, "auth_alg", "LEAP"); + wpa_set_data (conn_name, "eap", "LEAP"); + wpa_set_data (conn_name, "identity", (gchar *) + nm_setting_wireless_security_get_leap_username + (s_wsec)); + wpa_set_data (conn_name, "password", (gchar *) + nm_setting_wireless_security_get_leap_password + (s_wsec)); + *no_8021x = TRUE; + } + } else + wpa_set_data (conn_name, "auth_alg", NULL); + + /* Default WEP TX key index */ + if (wep) { + tmp = + g_strdup_printf ("%d", + nm_setting_wireless_security_get_wep_tx_keyidx + (s_wsec)); + wpa_set_data (conn_name, "wep_tx_keyidx", tmp); + g_free (tmp); + } else + wpa_set_data (conn_name, "wep_tx_keyidx", NULL); + + /* WEP keys */ + for (i = 0; i < 4; i++) { + int length; + + key = nm_setting_wireless_security_get_wep_key (s_wsec, i); + if (!key) + continue; + tmp = g_strdup_printf ("wep_key%d", i); + length = strlen (key); + if (length == 10 || length == 26 || length == 58) + wpa_set_data (conn_name, tmp, (gchar *) key); + else { + gchar *tmp_key = g_strdup_printf ("\"%s\"", key); + + wpa_set_data (conn_name, tmp, tmp_key); + g_free (tmp_key); + } + g_free (tmp); + } + + /* WPA Pairwise ciphers */ + wpa_set_data (conn_name, "pairwise", NULL); + str = g_string_new (NULL); + num = nm_setting_wireless_security_get_num_pairwise (s_wsec); + for (i = 0; i < num; i++) { + if (i > 0) + g_string_append_c (str, ' '); + cipher = nm_setting_wireless_security_get_pairwise (s_wsec, i); + tmp = g_ascii_strup (cipher, -1); + g_string_append (str, tmp); + g_free (tmp); + } + if (strlen (str->str)) + wpa_set_data (conn_name, "pairwise", str->str); + g_string_free (str, TRUE); + + /* WPA Group ciphers */ + wpa_set_data (conn_name, "group", NULL); + str = g_string_new (NULL); + num = nm_setting_wireless_security_get_num_groups (s_wsec); + for (i = 0; i < num; i++) { + if (i > 0) + g_string_append_c (str, ' '); + cipher = nm_setting_wireless_security_get_group (s_wsec, i); + tmp = g_ascii_strup (cipher, -1); + g_string_append (str, tmp); + g_free (tmp); + } + if (strlen (str->str)) + wpa_set_data (conn_name, "group", str->str); + g_string_free (str, TRUE); + + /* WPA Passphrase */ + if (wpa) { + GString *quoted = NULL; + + psk = nm_setting_wireless_security_get_psk (s_wsec); + if (psk && (strlen (psk) != 64)) { + quoted = g_string_sized_new (strlen (psk) + 2); + g_string_append_c (quoted, '"'); + g_string_append (quoted, psk); + g_string_append_c (quoted, '"'); + } + /* psk will be lost here if we don't check it for NULL */ + if (psk) + wpa_set_data (conn_name, "psk", + quoted ? quoted->str : (gchar *) psk); + if (quoted) + g_string_free (quoted, TRUE); + } else + wpa_set_data (conn_name, "psk", NULL); + + return TRUE; +} + +/* remove old ssid and add new one*/ +static void +update_wireless_ssid (NMConnection *connection, + const char *conn_name, + const char *ssid, + gboolean hex) +{ + if(strcmp (conn_name, ssid)){ + ifnet_delete_network (conn_name); + wpa_delete_security (conn_name); + } + + ifnet_add_network (ssid, "wireless"); + wpa_add_security (ssid); +} + +static gboolean +write_wireless_setting (NMConnection *connection, + const char *conn_name, + gboolean *no_8021x, + const char **out_new_name, + GError **error) +{ + NMSettingWireless *s_wireless; + GBytes *ssid; + const guint8 *ssid_data; + gsize ssid_len; + const char *mac, *bssid, *mode; + char buf[33]; + guint32 mtu, i; + gboolean adhoc = FALSE, hex_ssid = FALSE; + gchar *ssid_str, *tmp; + + s_wireless = nm_connection_get_setting_wireless (connection); + if (!s_wireless) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Missing '%s' setting", + NM_SETTING_WIRELESS_SETTING_NAME); + return FALSE; + } + + ssid = nm_setting_wireless_get_ssid (s_wireless); + if (!ssid) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Missing SSID in '%s' setting", + NM_SETTING_WIRELESS_SETTING_NAME); + return FALSE; + } + ssid_data = g_bytes_get_data (ssid, &ssid_len); + if (!ssid_len || ssid_len > 32) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Invalid SSID in '%s' setting", + NM_SETTING_WIRELESS_SETTING_NAME); + return FALSE; + } + + /* If the SSID contains any non-alnum characters, we need to use + * the hex notation of the SSID instead. (Because openrc doesn't + * support these characters, see bug #356337) + */ + for (i = 0; i < ssid_len; i++) { + if (!g_ascii_isalnum (ssid_data[i])) { + hex_ssid = TRUE; + break; + } + } + + if (hex_ssid) { + GString *str; + + /* Hex SSIDs don't get quoted */ + str = g_string_sized_new (ssid_len * 2 + 3); + g_string_append (str, "0x"); + for (i = 0; i < ssid_len; i++) + g_string_append_printf (str, "%02X", ssid_data[i]); + update_wireless_ssid (connection, conn_name, str->str, hex_ssid); + ssid_str = g_string_free (str, FALSE); + } else { + /* Printable SSIDs get quoted */ + memset (buf, 0, sizeof (buf)); + memcpy (buf, ssid_data, ssid_len); + g_strstrip (buf); + update_wireless_ssid (connection, conn_name, buf, hex_ssid); + ssid_str = g_strdup (buf); + } + + ifnet_set_data (ssid_str, "mac", NULL); + mac = nm_setting_wireless_get_mac_address (s_wireless); + if (mac) + ifnet_set_data (ssid_str, "mac", mac); + + ifnet_set_data (ssid_str, "mtu", NULL); + mtu = nm_setting_wireless_get_mtu (s_wireless); + if (mtu) { + tmp = g_strdup_printf ("%u", mtu); + ifnet_set_data (ssid_str, "mtu", tmp); + g_free (tmp); + } + + ifnet_set_data (ssid_str, "mode", NULL); + mode = nm_setting_wireless_get_mode (s_wireless); + if (!mode || !strcmp (mode, "infrastructure")) { + wpa_set_data (ssid_str, "mode", "0"); + } else if (!strcmp (mode, "adhoc")) { + wpa_set_data (ssid_str, "mode", "1"); + adhoc = TRUE; + } else { + nm_log_warn (LOGD_SETTINGS, "Invalid mode '%s' in '%s' setting", + mode, NM_SETTING_WIRELESS_SETTING_NAME); + return FALSE; + } + + wpa_set_data (ssid_str, "bssid", NULL); + bssid = nm_setting_wireless_get_bssid (s_wireless); + if (bssid) + wpa_set_data (ssid_str, "bssid", bssid); + + if (nm_connection_get_setting_wireless_security (connection)) { + if (!write_wireless_security_setting + (connection, ssid_str, adhoc, no_8021x, error)) + return FALSE; + } else + wpa_delete_security (ssid_str); + + if (out_new_name) + *out_new_name = ifnet_get_data (ssid_str, "name"); + g_free (ssid_str); + return TRUE; +} + +static gboolean +write_wired_setting (NMConnection *connection, + const char *conn_name, + GError **error) +{ + NMSettingWired *s_wired; + const char *mac; + char *tmp; + guint32 mtu; + + s_wired = nm_connection_get_setting_wired (connection); + if (!s_wired) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Missing '%s' setting", + NM_SETTING_WIRED_SETTING_NAME); + return FALSE; + } + + ifnet_set_data (conn_name, "mac", NULL); + mac = nm_setting_wired_get_mac_address (s_wired); + if (mac) + ifnet_set_data (conn_name, "mac", mac); + + ifnet_set_data (conn_name, "mtu", NULL); + mtu = nm_setting_wired_get_mtu (s_wired); + if (mtu) { + tmp = g_strdup_printf ("%u", mtu); + ifnet_set_data (conn_name, "mtu", tmp); + g_free (tmp); + } + //FIXME may add connection type in future + //ifnet_set_data (conn_name, "TYPE", TYPE_ETHERNET); + + return TRUE; +} + +static gboolean +write_ip4_setting (NMConnection *connection, const char *conn_name, GError **error) +{ + NMSettingIPConfig *s_ip4; + const char *value; + guint32 i, num; + GString *searches; + GString *ips; + GString *routes; + GString *dns; + gboolean success = FALSE; + + s_ip4 = nm_connection_get_setting_ip4_config (connection); + if (!s_ip4) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Missing '%s' setting", + NM_SETTING_IP4_CONFIG_SETTING_NAME); + return FALSE; + } + routes = g_string_new (NULL); + + value = nm_setting_ip_config_get_method (s_ip4); + g_assert (value); + if (!strcmp (value, NM_SETTING_IP4_CONFIG_METHOD_MANUAL)) { + + num = nm_setting_ip_config_get_num_addresses (s_ip4); + ips = g_string_new (NULL); + /* IPv4 addresses */ + for (i = 0; i < num; i++) { + NMIPAddress *addr; + + addr = nm_setting_ip_config_get_address (s_ip4, i); + + g_string_append_printf (ips, "\"%s/%u", + nm_ip_address_get_address (addr), + nm_ip_address_get_prefix (addr)); + + /* only the first gateway will be written */ + if (i == 0 && nm_setting_ip_config_get_gateway (s_ip4)) { + g_string_append_printf (routes, + "\"default via %s\" ", + nm_setting_ip_config_get_gateway (s_ip4)); + } + } + ifnet_set_data (conn_name, "config", ips->str); + g_string_free (ips, TRUE); + } else if (!strcmp (value, NM_SETTING_IP4_CONFIG_METHOD_SHARED)) + ifnet_set_data (conn_name, "config", "shared"); + else if (!strcmp (value, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL)) + ifnet_set_data (conn_name, "config", "autoip"); + else + ifnet_set_data (conn_name, "config", "dhcp"); + + /* DNS Servers */ + num = nm_setting_ip_config_get_num_dns (s_ip4); + if (num > 0) { + dns = g_string_new (NULL); + for (i = 0; i < num; i++) { + const char *ip; + + ip = nm_setting_ip_config_get_dns (s_ip4, i); + g_string_append_printf (dns, " %s", ip); + } + ifnet_set_data (conn_name, "dns_servers", dns->str); + g_string_free (dns, TRUE); + } else + ifnet_set_data (conn_name, "dns_servers", NULL); + + /* DNS Searches */ + num = nm_setting_ip_config_get_num_dns_searches (s_ip4); + if (num > 0) { + searches = g_string_new (NULL); + for (i = 0; i < num; i++) { + if (i > 0) + g_string_append_c (searches, ' '); + g_string_append (searches, + nm_setting_ip_config_get_dns_search + (s_ip4, i)); + } + ifnet_set_data (conn_name, "dns_search", searches->str); + g_string_free (searches, TRUE); + } else + ifnet_set_data (conn_name, "dns_search", NULL); + /* FIXME Will be implemented when configuration supports it + if (!strcmp(value, NM_SETTING_IP4_CONFIG_METHOD_AUTO)) { + value = nm_setting_ip_config_get_dhcp_hostname(s_ip4); + if (value) + ifnet_set_data(conn_name, "DHCP_HOSTNAME", value, + FALSE); + + value = nm_setting_ip_config_get_dhcp_client_id(s_ip4); + if (value) + ifnet_set_data(conn_name, "DHCP_CLIENT_ID", value, + FALSE); + } + */ + + /* Static routes */ + num = nm_setting_ip_config_get_num_routes (s_ip4); + if (num > 0) { + for (i = 0; i < num; i++) { + NMIPRoute *route; + const char *next_hop; + + route = nm_setting_ip_config_get_route (s_ip4, i); + + next_hop = nm_ip_route_get_next_hop (route); + if (!next_hop) + next_hop = "0.0.0.0"; + + g_string_append_printf (routes, "\"%s/%u via %s\" ", + nm_ip_route_get_dest (route), + nm_ip_route_get_prefix (route), + next_hop); + } + } + if (routes->len > 0) + ifnet_set_data (conn_name, "routes", routes->str); + else + ifnet_set_data (conn_name, "routes", NULL); + g_string_free (routes, TRUE); + + success = TRUE; + + return success; +} + +static void +write_route6_file (NMSettingIPConfig *s_ip6, const char *conn_name) +{ + NMIPRoute *route; + const char *next_hop; + guint32 i, num; + GString *routes_string; + const char *old_routes; + + g_return_if_fail (s_ip6 != NULL); + num = nm_setting_ip_config_get_num_routes (s_ip6); + if (num == 0) + return; + + old_routes = ifnet_get_data (conn_name, "routes"); + routes_string = g_string_new (old_routes); + if (old_routes) + g_string_append (routes_string, "\" "); + for (i = 0; i < num; i++) { + route = nm_setting_ip_config_get_route (s_ip6, i); + + next_hop = nm_ip_route_get_next_hop (route); + if (!next_hop) + next_hop = "::"; + + g_string_append_printf (routes_string, "\"%s/%u via %s\" ", + nm_ip_route_get_dest (route), + nm_ip_route_get_prefix (route), + next_hop); + } + if (num > 0) + ifnet_set_data (conn_name, "routes", routes_string->str); + g_string_free (routes_string, TRUE); +} + +static gboolean +write_ip6_setting (NMConnection *connection, const char *conn_name, GError **error) +{ + NMSettingIPConfig *s_ip6; + const char *value; + guint32 i, num; + GString *searches; + NMIPAddress *addr; + + s_ip6 = nm_connection_get_setting_ip6_config (connection); + if (!s_ip6) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Missing '%s' setting", + NM_SETTING_IP6_CONFIG_SETTING_NAME); + return FALSE; + } + + value = nm_setting_ip_config_get_method (s_ip6); + g_assert (value); + if (!strcmp (value, NM_SETTING_IP6_CONFIG_METHOD_IGNORE)) { + ifnet_set_data (conn_name, "enable_ipv6", "false"); + return TRUE; + } else if (!strcmp (value, NM_SETTING_IP6_CONFIG_METHOD_MANUAL)) { + /* nothing to do now */ + } else { + // if (!strcmp(value, NM_SETTING_IP6_CONFIG_METHOD_AUTO)) { + const char *config = ifnet_get_data (conn_name, "config"); + gchar *tmp; + + if (!config) + tmp = g_strdup_printf ("dhcp6"); + else + tmp = g_strdup_printf ("%s\" \"dhcp6\"", config); + ifnet_set_data (conn_name, "config", tmp); + g_free (tmp); + } + /* else if (!strcmp(value, NM_SETTING_IP6_CONFIG_METHOD_MANUAL)) { + } else if (!strcmp(value, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL)) { + } else if (!strcmp(value, NM_SETTING_IP6_CONFIG_METHOD_SHARED)) { + } */ + + /* Remember to set IPv6 enabled */ + ifnet_set_data (conn_name, "enable_ipv6", "true"); + + if (!strcmp (value, NM_SETTING_IP6_CONFIG_METHOD_MANUAL)) { + const char *config = ifnet_get_data (conn_name, "config"); + gchar *tmp; + GString *ip_str; + + if (!config) + config = ""; + num = nm_setting_ip_config_get_num_addresses (s_ip6); + + /* IPv6 addresses */ + ip_str = g_string_new (NULL); + for (i = 0; i < num; i++) { + addr = nm_setting_ip_config_get_address (s_ip6, i); + + g_string_append_printf (ip_str, "\"%s/%u\"", + nm_ip_address_get_address (addr), + nm_ip_address_get_prefix (addr)); + } + tmp = g_strdup_printf ("%s\" %s", config, ip_str->str); + ifnet_set_data (conn_name, "config", tmp); + g_free (tmp); + g_string_free (ip_str, TRUE); + } + + /* DNS Servers */ + num = nm_setting_ip_config_get_num_dns (s_ip6); + if (num > 0) { + const char *dns_servers = ifnet_get_data (conn_name, "dns_servers"); + gchar *tmp; + GString *dns_string = g_string_new (NULL); + const char *dns; + + if (!dns_servers) + dns_servers = ""; + for (i = 0; i < num; i++) { + dns = nm_setting_ip_config_get_dns (s_ip6, i); + + if (!strstr (dns_servers, dns)) + g_string_append_printf (dns_string, "%s ", dns); + } + tmp = g_strdup_printf ("%s %s", dns_servers, dns_string->str); + ifnet_set_data (conn_name, "dns_servers", tmp); + g_free (tmp); + g_string_free (dns_string, TRUE); + + } else + /* DNS Searches */ + num = nm_setting_ip_config_get_num_dns_searches (s_ip6); + if (num > 0) { + const char *ip4_domains; + + ip4_domains = ifnet_get_data (conn_name, "dns_search"); + if (!ip4_domains) + ip4_domains = ""; + searches = g_string_new (ip4_domains); + for (i = 0; i < num; i++) { + const gchar *search = NULL; + + search = + nm_setting_ip_config_get_dns_search (s_ip6, i); + if (search && !strstr (searches->str, search)) { + if (searches->len > 0) + g_string_append_c (searches, ' '); + g_string_append (searches, search); + } + } + ifnet_set_data (conn_name, "dns_search", searches->str); + g_string_free (searches, TRUE); + } + + write_route6_file (s_ip6, conn_name); + return TRUE; +} + +static gboolean +write_pppoe_setting (const char *conn_name, NMSettingPppoe * s_pppoe) +{ + const gchar *value; + + value = nm_setting_pppoe_get_username (s_pppoe); + if (!value) { + return FALSE; + } + ifnet_set_data (conn_name, "username", (gchar *) value); + + value = nm_setting_pppoe_get_password (s_pppoe); + /* password could be NULL here */ + if (value) { + ifnet_set_data (conn_name, "password", (gchar *) value); + } + return TRUE; +} + +gboolean +ifnet_update_parsers_by_connection (NMConnection *connection, + const char *conn_name, + const char *config_file, + const char *wpa_file, + gchar **out_new_name, + gchar **out_backup, + GError **error) +{ + NMSettingConnection *s_con; + NMSettingIPConfig *s_ip6; + gboolean success = FALSE; + const char *type; + gboolean no_8021x = FALSE; + gboolean wired = FALSE, pppoe = TRUE; + const char *new_name = NULL; + + if (!ifnet_can_write_connection (connection, error)) + return FALSE; + + s_con = nm_connection_get_setting_connection (connection); + g_assert (s_con); + + type = nm_setting_connection_get_connection_type (s_con); + if (!type) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Missing connection type!"); + goto out; + } + + if (!strcmp (type, NM_SETTING_WIRED_SETTING_NAME)) { + /* Writing wired setting */ + if (!write_wired_setting (connection, conn_name, error)) + goto out; + wired = TRUE; + no_8021x = TRUE; + } else if (!strcmp (type, NM_SETTING_WIRELESS_SETTING_NAME)) { + /* Writing wireless setting */ + if (!write_wireless_setting (connection, conn_name, &no_8021x, &new_name, error)) + goto out; + } else if (!strcmp (type, NM_SETTING_PPPOE_SETTING_NAME)) { + NMSettingPppoe *s_pppoe; + + /* Writing pppoe setting */ + s_pppoe = nm_connection_get_setting_pppoe (connection); + if (!write_pppoe_setting (conn_name, s_pppoe)) + goto out; + pppoe = TRUE; + wired = TRUE; + no_8021x = TRUE; + } else { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_NOT_SUPPORTED, + "Can't write connection type '%s'", type); + goto out; + } + + /* connection name may have been updated; use it when writing out + * the rest of the settings. + */ + if (new_name) + conn_name = new_name; + + //FIXME wired connection doesn't support 8021x now + if (!no_8021x) { + if (!write_8021x_setting (connection, conn_name, wired, error)) + goto out; + } + + /* IPv4 Setting */ + if (!write_ip4_setting (connection, conn_name, error)) + goto out; + + s_ip6 = nm_connection_get_setting_ip6_config (connection); + if (s_ip6) { + /* IPv6 Setting */ + if (!write_ip6_setting (connection, conn_name, error)) + goto out; + } + + /* Connection Setting */ + ifnet_set_data (conn_name, "auto", + nm_setting_connection_get_autoconnect (s_con) ? "true" : "false"); + ifnet_set_data (conn_name, "uuid", nm_connection_get_uuid (connection)); + + /* Write changes to disk */ + success = ifnet_flush_to_file (config_file, out_backup); + if (success) + wpa_flush_to_file (wpa_file); + + if (out_new_name) + *out_new_name = g_strdup (conn_name); + +out: + return success; +} + +gboolean +ifnet_delete_connection_in_parsers (const char *conn_name, + const char *config_file, + const char *wpa_file, + gchar **out_backup) +{ + gboolean result = FALSE; + + ifnet_delete_network (conn_name); + result = ifnet_flush_to_file (config_file, out_backup); + if (result) { + /* connection may not have security information + * so simply ignore the return value*/ + wpa_delete_security (conn_name); + wpa_flush_to_file (wpa_file); + } + + return result; +} + +static void +check_unsupported_secrets (NMSetting *setting, + const char *key, + const GValue *value, + GParamFlags flags, + gpointer user_data) +{ + gboolean *unsupported_secret = user_data; + + if (flags & NM_SETTING_PARAM_SECRET) { + NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE; + + if (!nm_setting_get_secret_flags (setting, key, &secret_flags, NULL)) + g_return_if_reached (); + if (secret_flags != NM_SETTING_SECRET_FLAG_NONE) + *unsupported_secret = TRUE; + } +} + +gboolean +ifnet_can_write_connection (NMConnection *connection, GError **error) +{ + NMSettingConnection *s_con; + gboolean has_unsupported_secrets = FALSE; + + s_con = nm_connection_get_setting_connection (connection); + g_assert (s_con); + + /* If the connection is not available for all users, ignore + * it as this plugin only deals with System Connections */ + if (nm_setting_connection_get_num_permissions (s_con)) { + g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_NOT_SUPPORTED, + "The ifnet plugin does not support non-system-wide connections."); + return FALSE; + } + + /* Only support wired, wifi, and PPPoE */ + if ( !nm_connection_is_type (connection, NM_SETTING_WIRED_SETTING_NAME) + && !nm_connection_is_type (connection, NM_SETTING_WIRELESS_SETTING_NAME) + && !nm_connection_is_type (connection, NM_SETTING_PPPOE_SETTING_NAME)) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_NOT_SUPPORTED, + "The ifnet plugin cannot write the connection '%s' (type '%s')", + nm_connection_get_id (connection), + nm_setting_connection_get_connection_type (s_con)); + return FALSE; + } + + /* If the connection has flagged secrets, ignore + * it as this plugin does not deal with user agent service */ + nm_connection_for_each_setting_value (connection, + check_unsupported_secrets, + &has_unsupported_secrets); + if (has_unsupported_secrets) { + g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_NOT_SUPPORTED, + "The ifnet plugin only supports persistent system secrets."); + return FALSE; + } + + return TRUE; +} + +/* get the available wired name(eth*). */ +static gchar * +get_wired_name (void) +{ + int i = 0; + + for (; i < 256; i++) { + gchar *conn_name = g_strdup_printf ("eth%d", i); + + if (!ifnet_has_network (conn_name)) { + return conn_name; + } else + g_free (conn_name); + } + return NULL; +} + +/* get the available pppoe name(ppp*). */ +static gchar * +get_ppp_name (void) +{ + int i = 0; + + for (; i < 256; i++) { + gchar *conn_name = g_strdup_printf ("ppp%d", i); + + if (!ifnet_has_network (conn_name)) { + return conn_name; + } else + g_free (conn_name); + } + return NULL; +} + +/* get wireless ssid */ +static gchar * +get_wireless_name (NMConnection * connection) +{ + NMSettingWireless *s_wireless; + GBytes *ssid; + const guint8 *ssid_data; + gsize ssid_len; + gboolean hex_ssid = FALSE; + gchar *result = NULL; + char buf[33]; + int i = 0; + + s_wireless = nm_connection_get_setting_wireless (connection); + if (!s_wireless) + return NULL; + + ssid = nm_setting_wireless_get_ssid (s_wireless); + ssid_data = g_bytes_get_data (ssid, &ssid_len); + if (!ssid_len || ssid_len > 32) { + return NULL; + } + + for (i = 0; i < ssid_len; i++) { + if (!g_ascii_isprint (ssid_data[i])) { + hex_ssid = TRUE; + break; + } + } + + if (hex_ssid) { + GString *str; + + str = g_string_sized_new (ssid_len * 2 + 3); + g_string_append (str, "0x"); + for (i = 0; i < ssid_len; i++) + g_string_append_printf (str, "%02X", ssid_data[i]); + result = g_strdup (str->str); + g_string_free (str, TRUE); + } else { + memset (buf, 0, sizeof (buf)); + memcpy (buf, ssid_data, ssid_len); + result = g_strdup_printf ("%s", buf); + g_strstrip (result); + } + + return result; +} + +gboolean +ifnet_add_new_connection (NMConnection *connection, + const char *config_file, + const char *wpa_file, + gchar **out_new_name, + gchar **out_backup, + GError **error) +{ + NMSettingConnection *s_con; + gboolean success = FALSE; + const char *type; + gchar *new_type, *new_name = NULL; + + if (!ifnet_can_write_connection (connection, error)) + return FALSE; + + s_con = nm_connection_get_setting_connection (connection); + g_assert (s_con); + type = nm_setting_connection_get_connection_type (s_con); + g_assert (type); + + nm_log_info (LOGD_SETTINGS, "Adding %s connection", type); + + /* get name and type + * Wireless type: wireless + * Wired type: wired + * PPPoE type: ppp*/ + if (!strcmp (type, NM_SETTING_WIRED_SETTING_NAME)) { + new_name = get_wired_name (); + if (!new_name) + goto out; + new_type = "wired"; + } else if (!strcmp (type, NM_SETTING_WIRELESS_SETTING_NAME)) { + new_name = get_wireless_name (connection); + new_type = "wireless"; + } else if (!strcmp (type, NM_SETTING_PPPOE_SETTING_NAME)) { + new_name = get_ppp_name (); + if (!new_name) + goto out; + new_type = "ppp"; + } else { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_NOT_SUPPORTED, + "Can't write connection type '%s'", type); + goto out; + } + + if (ifnet_add_network (new_name, new_type)) { + success = ifnet_update_parsers_by_connection (connection, + new_name, + config_file, + wpa_file, + NULL, + out_backup, + error); + } + + nm_log_info (LOGD_SETTINGS, "Added new connection: %s, result: %s", + new_name, success ? "success" : "fail"); + +out: + if (!success || !out_new_name) + g_free (new_name); + else if (out_new_name) + *out_new_name = new_name; + return success; +} + |