/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* * Mu Qiao * * 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 #include #include #include #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; }