diff options
Diffstat (limited to 'src')
46 files changed, 2161 insertions, 634 deletions
diff --git a/src/NetworkManager.conf b/src/NetworkManager.conf index b256a444..15b11b7e 100644 --- a/src/NetworkManager.conf +++ b/src/NetworkManager.conf @@ -10,6 +10,19 @@ send_interface="org.freedesktop.NetworkManager.PPP"/> <allow send_interface="org.freedesktop.NetworkManager.SecretAgent"/> + + <!-- Allow NM to talk to known VPN plugins; due to a bug in + the D-Bus daemon, when a plugin is installed and the user + immediately tries to use it, the VPN plugin's rules aren't + always loaded into dbus-daemon. Those rules allow NM to + talk to the plugin. Oops. Work around that by explicitly + allowing NM to talk to VPN plugins here. + --> + <allow send_destination="org.freedesktop.NetworkManager.openconnect"/> + <allow send_destination="org.freedesktop.NetworkManager.openswan"/> + <allow send_destination="org.freedesktop.NetworkManager.openvpn"/> + <allow send_destination="org.freedesktop.NetworkManager.pptp"/> + <allow send_destination="org.freedesktop.NetworkManager.vpnc"/> </policy> <policy at_console="true"> <allow send_destination="org.freedesktop.NetworkManager"/> diff --git a/src/NetworkManagerUtils.c b/src/NetworkManagerUtils.c index 5a6a61b6..f96f2aad 100644 --- a/src/NetworkManagerUtils.c +++ b/src/NetworkManagerUtils.c @@ -39,6 +39,9 @@ #include "nm-setting-connection.h" #include "nm-setting-ip4-config.h" #include "nm-setting-ip6-config.h" +#include "nm-setting-wireless.h" +#include "nm-setting-wireless-security.h" +#include "nm-manager-auth.h" #include <netlink/addr.h> #include <netinet/in.h> @@ -709,6 +712,33 @@ nm_match_spec_s390_subchannels (const GSList *specs, const char *subchannels) return FALSE; } +const char * +nm_utils_get_shared_wifi_permission (NMConnection *connection) +{ + NMSettingWireless *s_wifi; + NMSettingWirelessSecurity *s_wsec; + NMSettingIP4Config *s_ip4; + const char *method = NULL; + + s_ip4 = nm_connection_get_setting_ip4_config (connection); + if (s_ip4) + method = nm_setting_ip4_config_get_method (s_ip4); + + if (g_strcmp0 (method, NM_SETTING_IP4_CONFIG_METHOD_SHARED) != 0) + return NULL; /* Not shared */ + + s_wifi = nm_connection_get_setting_wireless (connection); + if (s_wifi) { + s_wsec = nm_connection_get_setting_wireless_security (connection); + if (nm_setting_wireless_get_security (s_wifi) || s_wsec) + return NM_AUTH_PERMISSION_WIFI_SHARE_PROTECTED; + else + return NM_AUTH_PERMISSION_WIFI_SHARE_OPEN; + } + + return NULL; +} + /*********************************/ static void diff --git a/src/NetworkManagerUtils.h b/src/NetworkManagerUtils.h index aaa267fb..1bf741ed 100644 --- a/src/NetworkManagerUtils.h +++ b/src/NetworkManagerUtils.h @@ -52,6 +52,7 @@ void nm_utils_call_dispatcher (const char *action, gboolean nm_match_spec_hwaddr (const GSList *specs, const char *hwaddr); gboolean nm_match_spec_s390_subchannels (const GSList *specs, const char *subchannels); +const char *nm_utils_get_shared_wifi_permission (NMConnection *connection); GHashTable *value_hash_create (void); void value_hash_add (GHashTable *hash, diff --git a/src/dns-manager/nm-dns-manager.c b/src/dns-manager/nm-dns-manager.c index fc582327..49cd74e5 100644 --- a/src/dns-manager/nm-dns-manager.c +++ b/src/dns-manager/nm-dns-manager.c @@ -165,7 +165,7 @@ merge_one_ip4_config (NMResolvConfData *rc, NMIP4Config *src) } static void -merge_one_ip6_config (NMResolvConfData *rc, NMIP6Config *src) +merge_one_ip6_config (NMResolvConfData *rc, NMIP6Config *src, const char *iface) { guint32 num, i; @@ -181,8 +181,15 @@ merge_one_ip6_config (NMResolvConfData *rc, NMIP6Config *src) if (inet_ntop (AF_INET, &(addr->s6_addr32[3]), buf, INET_ADDRSTRLEN) > 0) add_string_item (rc->nameservers, buf); } else { - if (inet_ntop (AF_INET6, addr, buf, INET6_ADDRSTRLEN) > 0) - add_string_item (rc->nameservers, buf); + if (inet_ntop (AF_INET6, addr, buf, INET6_ADDRSTRLEN) > 0) { + if (IN6_IS_ADDR_LINKLOCAL (addr) && strchr (buf, '%') == NULL) { + char *tmp; + tmp = g_strdup_printf ("%s%%%s", buf, iface); + add_string_item (rc->nameservers, tmp); + g_free (tmp); + } else + add_string_item (rc->nameservers, buf); + } } } @@ -594,9 +601,9 @@ update_dns (NMDnsManager *self, merge_one_ip4_config (&rc, priv->ip4_device_config); if (priv->ip6_vpn_config) - merge_one_ip6_config (&rc, priv->ip6_vpn_config); + merge_one_ip6_config (&rc, priv->ip6_vpn_config, iface); if (priv->ip6_device_config) - merge_one_ip6_config (&rc, priv->ip6_device_config); + merge_one_ip6_config (&rc, priv->ip6_device_config, iface); for (iter = priv->configs; iter; iter = g_slist_next (iter)) { if ( (iter->data == priv->ip4_vpn_config) @@ -612,7 +619,7 @@ update_dns (NMDnsManager *self, } else if (NM_IS_IP6_CONFIG (iter->data)) { NMIP6Config *config = NM_IP6_CONFIG (iter->data); - merge_one_ip6_config (&rc, config); + merge_one_ip6_config (&rc, config, iface); } else g_assert_not_reached (); } diff --git a/src/modem-manager/nm-modem.c b/src/modem-manager/nm-modem.c index 33f10789..1823e5e6 100644 --- a/src/modem-manager/nm-modem.c +++ b/src/modem-manager/nm-modem.c @@ -86,6 +86,17 @@ enum { static guint signals[LAST_SIGNAL] = { 0 }; +static void +update_mm_enabled (NMModem *self, gboolean new_enabled) +{ + NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self); + + if (priv->mm_enabled != new_enabled) { + priv->mm_enabled = new_enabled; + g_object_notify (G_OBJECT (self), NM_MODEM_ENABLED); + } +} + gboolean nm_modem_get_mm_enabled (NMModem *self) { @@ -822,8 +833,7 @@ get_mm_enabled_done (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_d } if (G_VALUE_HOLDS_BOOLEAN (&value)) { - NM_MODEM_GET_PRIVATE (self)->mm_enabled = g_value_get_boolean (&value); - g_object_notify (G_OBJECT (self), NM_MODEM_ENABLED); + update_mm_enabled (self, g_value_get_boolean (&value)); } else nm_log_warn (LOGD_MB, "failed get modem enabled state: unexpected reply type"); @@ -880,6 +890,9 @@ nm_modem_set_mm_enabled (NMModem *self, gboolean enabled) self, NULL, G_TYPE_BOOLEAN, enabled, G_TYPE_INVALID); + /* If we are disabling the modem, stop saying that it's enabled. */ + if (!enabled) + update_mm_enabled (self, enabled); } } @@ -898,8 +911,7 @@ modem_properties_changed (DBusGProxy *proxy, value = g_hash_table_lookup (props, "Enabled"); if (value && G_VALUE_HOLDS_BOOLEAN (value)) { - priv->mm_enabled = g_value_get_boolean (value); - g_object_notify (G_OBJECT (self), NM_MODEM_ENABLED); + update_mm_enabled (self, g_value_get_boolean (value)); } value = g_hash_table_lookup (props, "IpMethod"); diff --git a/src/nm-device-ethernet.c b/src/nm-device-ethernet.c index f470e090..35b73670 100644 --- a/src/nm-device-ethernet.c +++ b/src/nm-device-ethernet.c @@ -34,6 +34,7 @@ #include <unistd.h> #include <linux/if.h> #include <errno.h> +#include <netinet/ether.h> #include <gudev/gudev.h> @@ -881,6 +882,8 @@ real_get_best_auto_connection (NMDevice *dev, NMSettingWired *s_wired; const char *connection_type; gboolean is_pppoe = FALSE; + const GSList *mac_blacklist, *mac_blacklist_iter; + gboolean mac_blacklist_found = FALSE; s_con = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION); g_assert (s_con); @@ -909,6 +912,25 @@ real_get_best_auto_connection (NMDevice *dev, mac = nm_setting_wired_get_mac_address (s_wired); if (try_mac && mac && memcmp (mac->data, &priv->perm_hw_addr, ETH_ALEN)) continue; + + /* Check for MAC address blacklist */ + mac_blacklist = nm_setting_wired_get_mac_address_blacklist (s_wired); + for (mac_blacklist_iter = mac_blacklist; mac_blacklist_iter; + mac_blacklist_iter = g_slist_next (mac_blacklist_iter)) { + struct ether_addr addr; + + if (!ether_aton_r (mac_blacklist_iter->data, &addr)) { + g_warn_if_reached (); + continue; + } + if (memcmp (&addr, &priv->perm_hw_addr, ETH_ALEN) == 0) { + mac_blacklist_found = TRUE; + break; + } + } + /* Found device MAC address in the blacklist - do not use this connection */ + if (mac_blacklist_found) + continue; } return connection; @@ -1605,6 +1627,7 @@ real_check_connection_compatible (NMDevice *device, gboolean is_pppoe = FALSE; const GByteArray *mac; gboolean try_mac = TRUE; + const GSList *mac_blacklist, *mac_blacklist_iter; s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION)); g_assert (s_con); @@ -1645,6 +1668,25 @@ real_check_connection_compatible (NMDevice *device, "The connection's MAC address did not match this device."); return FALSE; } + + /* Check for MAC address blacklist */ + mac_blacklist = nm_setting_wired_get_mac_address_blacklist (s_wired); + for (mac_blacklist_iter = mac_blacklist; mac_blacklist_iter; + mac_blacklist_iter = g_slist_next (mac_blacklist_iter)) { + struct ether_addr addr; + + if (!ether_aton_r (mac_blacklist_iter->data, &addr)) { + g_warn_if_reached (); + continue; + } + if (memcmp (&addr, &priv->perm_hw_addr, ETH_ALEN) == 0) { + g_set_error (error, + NM_ETHERNET_ERROR, NM_ETHERNET_ERROR_CONNECTION_INCOMPATIBLE, + "The connection's MAC address (%s) is blacklisted in %s.", + (char *) mac_blacklist_iter->data, NM_SETTING_WIRED_MAC_ADDRESS_BLACKLIST); + return FALSE; + } + } } // FIXME: check bitrate against device capabilities diff --git a/src/nm-device-olpc-mesh.c b/src/nm-device-olpc-mesh.c index 8c8f9d0d..88322344 100644 --- a/src/nm-device-olpc-mesh.c +++ b/src/nm-device-olpc-mesh.c @@ -104,7 +104,6 @@ struct _NMDeviceOlpcMeshPrivate gint8 num_freqs; guint32 freqs[IW_MAX_FREQUENCIES]; - guint8 we_version; gboolean up; NMDevice * companion; @@ -147,44 +146,7 @@ nm_olpc_mesh_error_get_type (void) static guint32 real_get_generic_capabilities (NMDevice *dev) { - int fd; - guint32 caps = NM_DEVICE_CAP_NONE; - struct iw_range range; - struct iwreq wrq; - const char *iface = nm_device_get_iface (dev); - - /* Check for Wireless Extensions support >= 16 for wireless devices */ - - fd = socket (PF_INET, SOCK_DGRAM, 0); - if (fd < 0) { - nm_log_warn (LOGD_OLPC_MESH, "couldn't open control socket."); - goto out; - } - - memset (&wrq, 0, sizeof (struct iwreq)); - memset (&range, 0, sizeof (struct iw_range)); - strncpy (wrq.ifr_name, iface, IFNAMSIZ); - wrq.u.data.pointer = (caddr_t) ⦥ - wrq.u.data.length = sizeof (struct iw_range); - - if (ioctl (fd, SIOCGIWRANGE, &wrq) < 0) { - nm_log_warn (LOGD_OLPC_MESH, "couldn't get driver range information."); - goto out; - } - - if ((wrq.u.data.length < 300) || (range.we_version_compiled < 16)) { - nm_log_warn (LOGD_OLPC_MESH, - "(%s): driver's Wireless Extensions version (%d) is too old.", - iface, range.we_version_compiled); - goto out; - } else { - caps |= NM_DEVICE_CAP_NM_SUPPORTED; - } - -out: - if (fd >= 0) - close (fd); - return caps; + return NM_DEVICE_CAP_NM_SUPPORTED; } static void @@ -193,7 +155,6 @@ nm_device_olpc_mesh_init (NMDeviceOlpcMesh * self) NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self); priv->dispose_has_run = FALSE; - priv->we_version = 0; priv->companion = NULL; priv->stage1_waiting = FALSE; @@ -276,15 +237,24 @@ constructor (GType type, wrq.u.data.pointer = (caddr_t) ⦥ wrq.u.data.length = sizeof (struct iw_range); - if (ioctl (fd, SIOCGIWRANGE, &wrq) < 0) + if (ioctl (fd, SIOCGIWRANGE, &wrq) < 0) { + nm_log_info (LOGD_HW | LOGD_WIFI, "(%s): driver WEXT range request failed", + nm_device_get_iface (NM_DEVICE (self))); + goto error; + } + + if ((wrq.u.data.length < 300) || (range.we_version_compiled < 21)) { + nm_log_info (LOGD_HW | LOGD_WIFI, + "(%s): driver WEXT version too old (got %d, expected >= 21)", + nm_device_get_iface (NM_DEVICE (self)), + range.we_version_compiled); goto error; + } priv->num_freqs = MIN (range.num_frequency, IW_MAX_FREQUENCIES); for (i = 0; i < priv->num_freqs; i++) priv->freqs[i] = iw_freq_to_uint32 (&range.freq[i]); - priv->we_version = range.we_version_compiled; - close (fd); /* shorter timeout for mesh connectivity */ @@ -453,7 +423,7 @@ create_socket_with_request (NMDevice *self, struct iwreq *req) g_return_val_if_fail (self != NULL, -1); sk = socket (AF_INET, SOCK_DGRAM, 0); - if (!sk) { + if (sk == -1) { nm_log_err (LOGD_OLPC_MESH, "Couldn't create socket: %d.", errno); return -1; } @@ -531,7 +501,6 @@ nm_device_olpc_mesh_set_channel (NMDeviceOlpcMesh *self, guint32 channel) static void nm_device_olpc_mesh_set_ssid (NMDeviceOlpcMesh *self, const GByteArray * ssid) { - NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self); int sk; struct iwreq wrq; const char * iface; @@ -541,7 +510,7 @@ nm_device_olpc_mesh_set_ssid (NMDeviceOlpcMesh *self, const GByteArray * ssid) g_return_if_fail (self != NULL); sk = socket (AF_INET, SOCK_DGRAM, 0); - if (!sk) { + if (sk == -1) { nm_log_err (LOGD_OLPC_MESH, "Couldn't create socket: %d.", errno); return; } @@ -554,24 +523,10 @@ nm_device_olpc_mesh_set_ssid (NMDeviceOlpcMesh *self, const GByteArray * ssid) memcpy (buf, ssid->data, MIN (sizeof (buf) - 1, len)); } wrq.u.essid.pointer = (caddr_t) buf; - - if (priv->we_version < 21) { - /* For historic reasons, set SSID length to include one extra - * character, C string nul termination, even though SSID is - * really an octet string that should not be presented as a C - * string. Some Linux drivers decrement the length by one and - * can thus end up missing the last octet of the SSID if the - * length is not incremented here. WE-21 changes this to - * explicitly require the length _not_ to include nul - * termination. */ - if (len) - len++; - } wrq.u.essid.length = len; wrq.u.essid.flags = (len > 0) ? 1 : 0; /* 1=enable SSID, 0=disable/any */ strncpy (wrq.ifr_name, iface, IFNAMSIZ); - if (ioctl (sk, SIOCSIWESSID, &wrq) < 0) { if (errno != ENODEV) { nm_log_err (LOGD_OLPC_MESH, "(%s): error setting SSID to '%s': %s", diff --git a/src/nm-device-wifi.c b/src/nm-device-wifi.c index 7a6e7528..278d9a06 100644 --- a/src/nm-device-wifi.c +++ b/src/nm-device-wifi.c @@ -33,6 +33,7 @@ #include <linux/sockios.h> #include <linux/ethtool.h> #include <sys/ioctl.h> +#include <netinet/ether.h> #include "nm-glib-compat.h" #include "nm-device.h" @@ -55,6 +56,7 @@ #include "nm-setting-ip4-config.h" #include "nm-setting-ip6-config.h" #include "nm-system.h" +#include "nm-settings-connection.h" static gboolean impl_device_get_access_points (NMDeviceWifi *device, GPtrArray **aps, @@ -153,7 +155,6 @@ struct _NMDeviceWifiPrivate { guint link_timeout_id; /* Static options from driver */ - guint8 we_version; guint32 capabilities; gboolean has_scan_capa_ssid; }; @@ -503,29 +504,14 @@ static guint32 real_get_generic_capabilities (NMDevice *dev) { int fd, err; - guint32 caps = NM_DEVICE_CAP_NONE, response_len = 0; + guint32 caps = NM_DEVICE_CAP_NONE; struct iwreq wrq; - struct iw_range range; const char *iface = nm_device_get_iface (dev); - gboolean success; - - memset (&range, 0, sizeof (struct iw_range)); - success = wireless_get_range (NM_DEVICE_WIFI (dev), &range, &response_len); - if (!success) - return NM_DEVICE_CAP_NONE; - - /* Check for Wireless Extensions support >= 16 for wireless devices */ - if ((response_len < 300) || (range.we_version_compiled < 16)) { - nm_log_err (LOGD_HW | LOGD_WIFI, - "(%s): driver's Wireless Extensions version (%d) is too old.", - iface, range.we_version_compiled); - return NM_DEVICE_CAP_NONE; - } fd = socket (PF_INET, SOCK_DGRAM, 0); if (fd < 0) { nm_log_err (LOGD_HW, "(%s): couldn't open control socket.", iface); - goto out; + return NM_DEVICE_CAP_NONE; } /* Cards that don't scan aren't supported */ @@ -538,7 +524,6 @@ real_get_generic_capabilities (NMDevice *dev) else caps |= NM_DEVICE_CAP_NM_SUPPORTED; -out: return caps; } @@ -548,11 +533,8 @@ out: NM_WIFI_DEVICE_CAP_RSN) static guint32 -get_wireless_capabilities (NMDeviceWifi *self, - iwrange * range, - guint32 data_len) +get_wireless_capabilities (NMDeviceWifi *self, iwrange *range) { - guint32 minlen; guint32 caps = NM_WIFI_DEVICE_CAP_NONE; const char * iface; @@ -561,39 +543,35 @@ get_wireless_capabilities (NMDeviceWifi *self, iface = nm_device_get_iface (NM_DEVICE (self)); - minlen = ((char *) &range->enc_capa) - (char *) range + sizeof (range->enc_capa); - /* All drivers should support WEP by default */ caps |= NM_WIFI_DEVICE_CAP_CIPHER_WEP40 | NM_WIFI_DEVICE_CAP_CIPHER_WEP104; - if ((data_len >= minlen) && range->we_version_compiled >= 18) { - if (range->enc_capa & IW_ENC_CAPA_CIPHER_TKIP) - caps |= NM_WIFI_DEVICE_CAP_CIPHER_TKIP; + if (range->enc_capa & IW_ENC_CAPA_CIPHER_TKIP) + caps |= NM_WIFI_DEVICE_CAP_CIPHER_TKIP; - if (range->enc_capa & IW_ENC_CAPA_CIPHER_CCMP) - caps |= NM_WIFI_DEVICE_CAP_CIPHER_CCMP; + if (range->enc_capa & IW_ENC_CAPA_CIPHER_CCMP) + caps |= NM_WIFI_DEVICE_CAP_CIPHER_CCMP; - if (range->enc_capa & IW_ENC_CAPA_WPA) - caps |= NM_WIFI_DEVICE_CAP_WPA; + if (range->enc_capa & IW_ENC_CAPA_WPA) + caps |= NM_WIFI_DEVICE_CAP_WPA; - if (range->enc_capa & IW_ENC_CAPA_WPA2) - caps |= NM_WIFI_DEVICE_CAP_RSN; + if (range->enc_capa & IW_ENC_CAPA_WPA2) + caps |= NM_WIFI_DEVICE_CAP_RSN; - /* Check for cipher support but not WPA support */ - if ( (caps & (NM_WIFI_DEVICE_CAP_CIPHER_TKIP | NM_WIFI_DEVICE_CAP_CIPHER_CCMP)) - && !(caps & (NM_WIFI_DEVICE_CAP_WPA | NM_WIFI_DEVICE_CAP_RSN))) { - nm_log_warn (LOGD_WIFI, "%s: device supports WPA ciphers but not WPA protocol; " - "WPA unavailable.", iface); - caps &= ~WPA_CAPS; - } + /* Check for cipher support but not WPA support */ + if ( (caps & (NM_WIFI_DEVICE_CAP_CIPHER_TKIP | NM_WIFI_DEVICE_CAP_CIPHER_CCMP)) + && !(caps & (NM_WIFI_DEVICE_CAP_WPA | NM_WIFI_DEVICE_CAP_RSN))) { + nm_log_warn (LOGD_WIFI, "%s: device supports WPA ciphers but not WPA protocol; " + "WPA unavailable.", iface); + caps &= ~WPA_CAPS; + } - /* Check for WPA support but not cipher support */ - if ( (caps & (NM_WIFI_DEVICE_CAP_WPA | NM_WIFI_DEVICE_CAP_RSN)) - && !(caps & (NM_WIFI_DEVICE_CAP_CIPHER_TKIP | NM_WIFI_DEVICE_CAP_CIPHER_CCMP))) { - nm_log_warn (LOGD_WIFI, "%s: device supports WPA protocol but not WPA ciphers; " - "WPA unavailable.", iface); - caps &= ~WPA_CAPS; - } + /* Check for WPA support but not cipher support */ + if ( (caps & (NM_WIFI_DEVICE_CAP_WPA | NM_WIFI_DEVICE_CAP_RSN)) + && !(caps & (NM_WIFI_DEVICE_CAP_CIPHER_TKIP | NM_WIFI_DEVICE_CAP_CIPHER_CCMP))) { + nm_log_warn (LOGD_WIFI, "%s: device supports WPA protocol but not WPA ciphers; " + "WPA unavailable.", iface); + caps &= ~WPA_CAPS; } return caps; @@ -665,8 +643,19 @@ constructor (GType type, memset (&range, 0, sizeof (struct iw_range)); success = wireless_get_range (NM_DEVICE_WIFI (object), &range, &response_len); - if (!success) + if (!success) { + nm_log_info (LOGD_HW | LOGD_WIFI, "(%s): driver WEXT range request failed", + nm_device_get_iface (NM_DEVICE (self))); goto error; + } + + if ((response_len < 300) || (range.we_version_compiled < 21)) { + nm_log_info (LOGD_HW | LOGD_WIFI, + "(%s): driver WEXT version too old (got %d, expected >= 21)", + nm_device_get_iface (NM_DEVICE (self)), + range.we_version_compiled); + goto error; + } priv->max_qual.qual = range.max_qual.qual; priv->max_qual.level = range.max_qual.level; @@ -677,8 +666,6 @@ constructor (GType type, for (i = 0; i < priv->num_freqs; i++) priv->freqs[i] = iw_freq_to_uint32 (&range.freq[i]); - priv->we_version = range.we_version_compiled; - /* Check for the ability to scan specific SSIDs. Until the scan_capa * field gets added to wireless-tools, need to work around that by casting * to the custom structure. @@ -698,7 +685,7 @@ constructor (GType type, } /* 802.11 wireless-specific capabilities */ - priv->capabilities = get_wireless_capabilities (self, &range, response_len); + priv->capabilities = get_wireless_capabilities (self, &range); /* Connect to the supplicant manager */ priv->supplicant.mgr = nm_supplicant_manager_get (); @@ -978,6 +965,31 @@ get_active_ap (NMDeviceWifi *self, } static void +update_seen_bssids_cache (NMDeviceWifi *self, NMAccessPoint *ap) +{ + NMActRequest *req; + NMConnection *connection; + + g_return_if_fail (NM_IS_DEVICE_WIFI (self)); + + if (ap == NULL) + return; + + /* Don't cache the BSSID for Ad-Hoc APs */ + if (nm_ap_get_mode (ap) != NM_802_11_MODE_INFRA) + return; + + if (nm_device_get_state (NM_DEVICE (self)) == NM_DEVICE_STATE_ACTIVATED) { + req = nm_device_get_act_request (NM_DEVICE (self)); + if (req) { + connection = nm_act_request_get_connection (req); + nm_settings_connection_add_seen_bssid (NM_SETTINGS_CONNECTION (connection), + nm_ap_get_address (ap)); + } + } +} + +static void set_current_ap (NMDeviceWifi *self, NMAccessPoint *new_ap) { NMDeviceWifiPrivate *priv; @@ -1003,6 +1015,9 @@ set_current_ap (NMDeviceWifi *self, NMAccessPoint *new_ap) */ priv->ap_list = g_slist_remove (priv->ap_list, new_ap); priv->ap_list = g_slist_prepend (priv->ap_list, new_ap); + + /* Update seen BSSIDs cache */ + update_seen_bssids_cache (self, priv->current_ap); } /* Unref old AP here to ensure object lives if new_ap == old_ap */ @@ -1303,6 +1318,7 @@ real_check_connection_compatible (NMDevice *device, NMSettingConnection *s_con; NMSettingWireless *s_wireless; const GByteArray *mac; + const GSList *mac_blacklist, *mac_blacklist_iter; s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION)); g_assert (s_con); @@ -1330,6 +1346,25 @@ real_check_connection_compatible (NMDevice *device, return FALSE; } + /* Check for MAC address blacklist */ + mac_blacklist = nm_setting_wireless_get_mac_address_blacklist (s_wireless); + for (mac_blacklist_iter = mac_blacklist; mac_blacklist_iter; + mac_blacklist_iter = g_slist_next (mac_blacklist_iter)) { + struct ether_addr addr; + + if (!ether_aton_r (mac_blacklist_iter->data, &addr)) { + g_warn_if_reached (); + continue; + } + if (memcmp (&addr, &priv->perm_hw_addr, ETH_ALEN) == 0) { + g_set_error (error, + NM_WIFI_ERROR, NM_WIFI_ERROR_CONNECTION_INCOMPATIBLE, + "The connection's MAC address (%s) is blacklisted in %s.", + (char *) mac_blacklist_iter->data, NM_SETTING_WIRELESS_MAC_ADDRESS_BLACKLIST); + return FALSE; + } + } + // FIXME: check channel/freq/band against bands the hardware supports // FIXME: check encryption against device capabilities // FIXME: check bitrate against device capabilities @@ -1574,6 +1609,8 @@ real_get_best_auto_connection (NMDevice *dev, NMSettingConnection *s_con; NMSettingWireless *s_wireless; const GByteArray *mac; + const GSList *mac_blacklist, *mac_blacklist_iter; + gboolean mac_blacklist_found = FALSE; NMSettingIP4Config *s_ip4; const char *method = NULL; @@ -1591,7 +1628,26 @@ real_get_best_auto_connection (NMDevice *dev, mac = nm_setting_wireless_get_mac_address (s_wireless); if (mac && memcmp (mac->data, &priv->perm_hw_addr, ETH_ALEN)) + continue; + + /* Check for MAC address blacklist */ + mac_blacklist = nm_setting_wireless_get_mac_address_blacklist (s_wireless); + for (mac_blacklist_iter = mac_blacklist; mac_blacklist_iter; + mac_blacklist_iter = g_slist_next (mac_blacklist_iter)) { + struct ether_addr addr; + + if (!ether_aton_r (mac_blacklist_iter->data, &addr)) { + g_warn_if_reached (); continue; + } + if (memcmp (&addr, &priv->perm_hw_addr, ETH_ALEN) == 0) { + mac_blacklist_found = TRUE; + break; + } + } + /* Found device MAC address in the blacklist - do not use this connection */ + if (mac_blacklist_found) + continue; /* Use the connection if it's a shared connection */ s_ip4 = (NMSettingIP4Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG); @@ -1820,7 +1876,7 @@ nm_device_wifi_get_ssid (NMDeviceWifi *self) priv = NM_DEVICE_WIFI_GET_PRIVATE (self); sk = socket (AF_INET, SOCK_DGRAM, 0); - if (!sk) { + if (sk == -1) { nm_log_err (LOGD_HW, "couldn't create socket: %d.", errno); return NULL; } @@ -1844,13 +1900,6 @@ nm_device_wifi_get_ssid (NMDeviceWifi *self) len = wrq.u.essid.length; if (!nm_utils_is_empty_ssid ((guint8 *) ssid, len)) { - /* Some drivers include nul termination in the SSID, so let's - * remove it here before further processing. WE-21 changes this - * to explicitly require the length _not_ to include nul - * termination. */ - if (len > 0 && ssid[len - 1] == '\0' && priv->we_version < 21) - len--; - priv->ssid = g_byte_array_sized_new (len); g_byte_array_append (priv->ssid, (const guint8 *) ssid, len); } @@ -2446,88 +2495,86 @@ remove_link_timeout (NMDeviceWifi *self) static gboolean link_timeout_cb (gpointer user_data) { - NMDevice * dev = NM_DEVICE (user_data); - NMDeviceWifi * self = NM_DEVICE_WIFI (dev); - NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - NMActRequest * req = NULL; - NMAccessPoint * ap = NULL; - NMConnection * connection; - const char * setting_name; - gboolean auth_enforced, encrypted = FALSE; - - g_assert (dev); + NMDevice *dev = NM_DEVICE (user_data); - priv->link_timeout_id = 0; + nm_log_warn (LOGD_WIFI, "(%s): link timed out.", nm_device_get_iface (dev)); - req = nm_device_get_act_request (dev); - ap = nm_device_wifi_get_activation_ap (self); - if (req == NULL || ap == NULL) { - /* shouldn't ever happen */ - nm_log_err (LOGD_WIFI, "couldn't get activation request or activation AP."); - if (nm_device_is_activating (dev)) { - cleanup_association_attempt (self, TRUE); - nm_device_state_changed (dev, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_NONE); - } - return FALSE; - } + NM_DEVICE_WIFI_GET_PRIVATE (dev)->link_timeout_id = 0; /* Disconnect event while activated; the supplicant hasn't been able * to reassociate within the timeout period, so the connection must * fail. */ - if (nm_device_get_state (dev) == NM_DEVICE_STATE_ACTIVATED) { + if (nm_device_get_state (dev) == NM_DEVICE_STATE_ACTIVATED) nm_device_state_changed (dev, NM_DEVICE_STATE_DISCONNECTED, NM_DEVICE_STATE_REASON_SUPPLICANT_TIMEOUT); - return FALSE; - } - /* Disconnect event during initial authentication and credentials - * ARE checked - we are likely to have wrong key. Ask the user for - * another one. + return FALSE; +} + +static gboolean +handle_authenticate_fail (NMDeviceWifi *self, guint32 new_state, guint32 old_state) +{ + NMDevice *device = NM_DEVICE (self); + NMSetting8021x *s_8021x; + NMSettingWirelessSecurity *s_wsec; + NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE; + NMActRequest *req; + NMConnection *connection; + const char *setting_name = NULL; + gboolean handled = FALSE; + + g_return_val_if_fail (new_state == NM_SUPPLICANT_INTERFACE_STATE_DISCONNECTED, FALSE); + + /* Only care about ASSOCIATED -> DISCONNECTED transitions since 802.1x stuff + * happens between the ASSOCIATED and AUTHENTICATED states. */ - if (nm_device_get_state (dev) != NM_DEVICE_STATE_CONFIG) - goto time_out; + if (old_state != NM_SUPPLICANT_INTERFACE_STATE_ASSOCIATED) + return FALSE; - connection = nm_act_request_get_connection (req); - if (!connection) - goto time_out; + req = nm_device_get_act_request (NM_DEVICE (self)); + g_return_val_if_fail (req != NULL, FALSE); - auth_enforced = ap_auth_enforced (connection, ap, &encrypted); - if (!encrypted || !auth_enforced) - goto time_out; + connection = nm_act_request_get_connection (req); + g_return_val_if_fail (connection != NULL, FALSE); - /* Drivers are still just too crappy, and emit too many disassociation - * events during connection. So for now, just let the driver and supplicant - * keep trying to associate, and don't ask for new secrets when we get - * disconnected during association. + /* If it's an 802.1x or LEAP connection with "always ask"/unsaved secrets + * then we need to ask again because it might be an OTP token and the PIN + * may have changed. */ - if (0) { + s_8021x = nm_connection_get_setting_802_1x (connection); + s_wsec = nm_connection_get_setting_wireless_security (connection); + + if (s_8021x) { + nm_setting_get_secret_flags (NM_SETTING (s_8021x), + NM_SETTING_802_1X_PASSWORD, + &secret_flags, + NULL); + setting_name = NM_SETTING_802_1X_SETTING_NAME; + } else if (s_wsec) { + nm_setting_get_secret_flags (NM_SETTING (s_wsec), + NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD, + &secret_flags, + NULL); + setting_name = NM_SETTING_WIRELESS_SECURITY_SETTING_NAME; + } + + if (setting_name && (secret_flags & NM_SETTING_SECRET_FLAG_NOT_SAVED)) { + NMSettingsGetSecretsFlags flags = NM_SETTINGS_GET_SECRETS_FLAG_ALLOW_INTERACTION + | NM_SETTINGS_GET_SECRETS_FLAG_REQUEST_NEW; + nm_connection_clear_secrets (connection); - setting_name = nm_connection_need_secrets (connection, NULL); - if (!setting_name) - goto time_out; - /* Association/authentication failed during association, probably have a - * bad encryption key and the authenticating entity (AP, RADIUS server, etc) - * denied the association due to bad credentials. - */ nm_log_info (LOGD_DEVICE | LOGD_WIFI, "Activation (%s/wireless): disconnected during association," - " asking for new key.", nm_device_get_iface (dev)); - cleanup_association_attempt (self, TRUE); - nm_device_state_changed (dev, NM_DEVICE_STATE_NEED_AUTH, NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT); - nm_act_request_get_secrets (req, - setting_name, - NM_SETTINGS_GET_SECRETS_FLAG_REQUEST_NEW, - NULL, - wifi_secrets_cb, - self); + " asking for new key.", nm_device_get_iface (device)); - return FALSE; + cleanup_association_attempt (self, TRUE); + nm_device_state_changed (device, NM_DEVICE_STATE_NEED_AUTH, NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT); + nm_act_request_get_secrets (req, setting_name, flags, NULL, wifi_secrets_cb, self); + handled = TRUE; } -time_out: - nm_log_warn (LOGD_WIFI, "(%s): link timed out.", nm_device_get_iface (dev)); - return FALSE; + return handled; } static void @@ -2596,14 +2643,20 @@ supplicant_iface_state_cb (NMSupplicantInterface *iface, break; case NM_SUPPLICANT_INTERFACE_STATE_DISCONNECTED: if ((devstate == NM_DEVICE_STATE_ACTIVATED) || nm_device_is_activating (device)) { - /* Start the link timeout so we allow some time for reauthentication, - * use a longer timeout if we are scanning since some cards take a - * while to scan. + /* Disconnect during authentication means the 802.1x password is wrong */ + if (handle_authenticate_fail (self, new_state, old_state)) + break; + } + + if (devstate == NM_DEVICE_STATE_ACTIVATED) { + /* If it's a disconnect while activated then start the link timer + * to let the supplicant reconnect for a bit and if that doesn't + * work kill the connection and try something else. Allow a bit + * more time if the card is scanning since sometimes the link will + * drop while scanning and come back when the scan is done. */ - if (!priv->link_timeout_id) { - priv->link_timeout_id = g_timeout_add_seconds (scanning ? 30 : 15, - link_timeout_cb, self); - } + if (priv->link_timeout_id == 0) + priv->link_timeout_id = g_timeout_add_seconds (scanning ? 30 : 15, link_timeout_cb, self); } break; case NM_SUPPLICANT_INTERFACE_STATE_DOWN: @@ -3436,11 +3489,13 @@ activation_success_handler (NMDevice *dev) done: periodic_update (self); + /* Update seen BSSIDs cache with the connected AP */ + update_seen_bssids_cache (self, priv->current_ap); + /* Reset scan interval to something reasonable */ priv->scan_interval = SCAN_INTERVAL_MIN + (SCAN_INTERVAL_STEP * 2); } - static void activation_failure_handler (NMDevice *dev) { @@ -3565,6 +3620,10 @@ device_state_changed (NMDevice *device, } clear_aps = TRUE; break; + case NM_DEVICE_STATE_NEED_AUTH: + if (priv->supplicant.iface) + nm_supplicant_interface_disconnect (priv->supplicant.iface); + break; case NM_DEVICE_STATE_ACTIVATED: activation_success_handler (device); break; diff --git a/src/nm-device.c b/src/nm-device.c index 4b7ec5df..1301197a 100644 --- a/src/nm-device.c +++ b/src/nm-device.c @@ -2891,34 +2891,6 @@ check_connection_compatible (NMDeviceInterface *dev_iface, } static gboolean -device_activation_precheck (NMDevice *self, NMConnection *connection, GError **error) -{ - NMConnection *current_connection; - - g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); - g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE); - - if (nm_device_get_state (self) != NM_DEVICE_STATE_ACTIVATED) - return TRUE; - - if (!nm_device_is_activating (self)) - return TRUE; - - // FIXME: why not just check connection path & service? - current_connection = nm_act_request_get_connection (nm_device_get_act_request (self)); - if (nm_connection_compare (connection, current_connection, NM_SETTING_COMPARE_FLAG_EXACT)) { - /* Already activating or activated with the same connection */ - g_set_error (error, - NM_DEVICE_INTERFACE_ERROR, - NM_DEVICE_INTERFACE_ERROR_CONNECTION_ACTIVATING, - "%s", "Connection is already activating"); - return FALSE; - } - - return TRUE; -} - -static gboolean nm_device_activate (NMDeviceInterface *device, NMActRequest *req, GError **error) @@ -2926,9 +2898,21 @@ nm_device_activate (NMDeviceInterface *device, NMDevice *self = NM_DEVICE (device); NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - if (!device_activation_precheck (self, nm_act_request_get_connection (req), error)) { - g_assert (*error); - return FALSE; + /* Make sure this connection isn't activated already, or in the process of + * being activated. + */ + if ( nm_device_is_activating (self) + || (nm_device_get_state (self) == NM_DEVICE_STATE_ACTIVATED)) { + NMConnection *new = nm_act_request_get_connection (req); + NMConnection *current = nm_act_request_get_connection (priv->act_request); + + if (new == current) { + g_set_error_literal (error, + NM_DEVICE_INTERFACE_ERROR, + NM_DEVICE_INTERFACE_ERROR_CONNECTION_ACTIVATING, + "Connection is already activating"); + return FALSE; + } } priv->act_request = g_object_ref (req); diff --git a/src/nm-manager-auth.c b/src/nm-manager-auth.c index 9171663b..beed65ab 100644 --- a/src/nm-manager-auth.c +++ b/src/nm-manager-auth.c @@ -173,6 +173,37 @@ nm_auth_chain_get_data (NMAuthChain *self, const char *tag) return tmp ? tmp->data : NULL; } +/** + * nm_auth_chain_steal_data: + * @self: A #NMAuthChain. + * @tag: A "tag" uniquely identifying the data to steal. + * + * Removes the datum assocated with @tag from the chain's data associations, + * without invoking the association's destroy handler. The caller assumes + * ownership over the returned value. + * + * Returns: the datum originally associated with @tag + */ +gpointer +nm_auth_chain_steal_data (NMAuthChain *self, const char *tag) +{ + ChainData *tmp; + gpointer value = NULL; + + g_return_val_if_fail (self != NULL, NULL); + g_return_val_if_fail (tag != NULL, NULL); + + tmp = g_hash_table_lookup (self->data, tag); + if (tmp) { + g_hash_table_steal (self->data, tag); + value = tmp->data; + /* Make sure the destroy handler isn't called when freeing */ + tmp->destroy = NULL; + free_data (tmp); + } + return value; +} + void nm_auth_chain_set_data (NMAuthChain *self, const char *tag, @@ -309,7 +340,8 @@ auth_call_schedule_early_finish (AuthCall *call, GError *error) { if (!call->chain->error) call->chain->error = error; - call->idle_id = g_idle_add ((GSourceFunc) auth_call_complete, call); + if (!call->idle_id) + call->idle_id = g_idle_add ((GSourceFunc) auth_call_complete, call); } #if WITH_POLKIT @@ -544,45 +576,82 @@ typedef struct { gpointer changed_data; } PkChangedInfo; +static GSList *funcs = NULL; + #if WITH_POLKIT static void -pk_authority_changed_cb (GObject *object, PkChangedInfo *info) +pk_authority_changed_cb (GObject *object, gpointer unused) { - info->changed_callback (info->changed_data); + GSList *iter; + + for (iter = funcs; iter; iter = g_slist_next (iter)) { + PkChangedInfo *info = iter->data; + + info->changed_callback (info->changed_data); + } } #endif void -nm_auth_set_changed_func (GDestroyNotify callback, gpointer callback_data) +nm_auth_changed_func_register (GDestroyNotify callback, gpointer callback_data) { #if WITH_POLKIT - static PkChangedInfo info = { NULL, NULL }; - static guint32 changed_id = 0; PolkitAuthority *authority; + static guint32 changed_id = 0; +#endif + PkChangedInfo *info; + GSList *iter; + gboolean found = FALSE; +#if WITH_POLKIT authority = pk_authority_get (); if (!authority) return; - if (callback == NULL) { - /* Clearing the callback */ - info.changed_callback = NULL; - info.changed_data = NULL; - g_signal_handler_disconnect (authority, changed_id); - changed_id = 0; - } else { - info.changed_callback = callback; - info.changed_data= callback_data; - - if (changed_id == 0) { - changed_id = g_signal_connect (authority, - "changed", - G_CALLBACK (pk_authority_changed_cb), - &info); + /* Hook up the changed signal the first time a callback is registered */ + if (changed_id == 0) { + changed_id = g_signal_connect (authority, + "changed", + G_CALLBACK (pk_authority_changed_cb), + &funcs); + } +#endif + + /* No duplicates */ + for (iter = funcs; iter; iter = g_slist_next (iter)) { + info = iter->data; + if ((callback == info->changed_callback) && (callback_data == info->changed_data)) { + found = TRUE; + break; } } + g_warn_if_fail (found == FALSE); + if (found == FALSE) { + info = g_malloc0 (sizeof (*info)); + info->changed_callback = callback; + info->changed_data = callback_data; + funcs = g_slist_append (funcs, info); + } + +#if WITH_POLKIT g_object_unref (authority); #endif } +void +nm_auth_changed_func_unregister (GDestroyNotify callback, gpointer callback_data) +{ + GSList *iter; + + for (iter = funcs; iter; iter = g_slist_next (iter)) { + PkChangedInfo *info = iter->data; + + if ((callback == info->changed_callback) && (callback_data == info->changed_data)) { + g_free (info); + funcs = g_slist_delete_link (funcs, iter); + break; + } + } +} + diff --git a/src/nm-manager-auth.h b/src/nm-manager-auth.h index ad14d306..15317c71 100644 --- a/src/nm-manager-auth.h +++ b/src/nm-manager-auth.h @@ -70,6 +70,8 @@ NMAuthChain *nm_auth_chain_new_dbus_sender (const char *dbus_sender, gpointer nm_auth_chain_get_data (NMAuthChain *chain, const char *tag); +gpointer nm_auth_chain_steal_data (NMAuthChain *chain, const char *tag); + void nm_auth_chain_set_data (NMAuthChain *chain, const char *tag, gpointer data, @@ -102,7 +104,9 @@ gboolean nm_auth_uid_in_acl (NMConnection *connection, gulong uid, char **out_error_desc); -void nm_auth_set_changed_func (GDestroyNotify callback, gpointer callback_data); +void nm_auth_changed_func_register (GDestroyNotify callback, gpointer callback_data); + +void nm_auth_changed_func_unregister (GDestroyNotify callback, gpointer callback_data); #endif /* NM_MANAGER_AUTH_H */ diff --git a/src/nm-manager.c b/src/nm-manager.c index 961c9d8c..f5782254 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -63,6 +63,7 @@ #include "nm-settings-connection.h" #include "nm-manager-auth.h" #include "NetworkManagerUtils.h" +#include "nm-utils.h" #define NM_AUTOIP_DBUS_SERVICE "org.freedesktop.nm_avahi_autoipd" #define NM_AUTOIP_DBUS_IFACE "org.freedesktop.nm_avahi_autoipd" @@ -172,6 +173,7 @@ struct PendingActivation { DBusGMethodInvocation *context; PendingActivationFunc callback; NMAuthChain *chain; + const char *wifi_shared_permission; char *connection_path; NMConnection *connection; @@ -739,17 +741,15 @@ pending_activation_new (NMManager *manager, } static void -pending_auth_net_done (NMAuthChain *chain, - GError *error, - DBusGMethodInvocation *context, - gpointer user_data) +pending_auth_done (NMAuthChain *chain, + GError *error, + DBusGMethodInvocation *context, + gpointer user_data) { PendingActivation *pending = user_data; NMAuthCallResult result; GError *tmp_error = NULL; - pending->chain = NULL; - /* Caller has had a chance to obtain authorization, so we only need to * check for 'yes' here. */ @@ -758,10 +758,23 @@ pending_auth_net_done (NMAuthChain *chain, tmp_error = g_error_new_literal (NM_MANAGER_ERROR, NM_MANAGER_ERROR_PERMISSION_DENIED, "Not authorized to control networking."); + goto out; + } + + if (pending->wifi_shared_permission) { + result = nm_auth_chain_get_result (chain, pending->wifi_shared_permission); + if (result != NM_AUTH_CALL_RESULT_YES) { + tmp_error = g_error_new_literal (NM_MANAGER_ERROR, + NM_MANAGER_ERROR_PERMISSION_DENIED, + "Not authorized to share connections via wifi."); + goto out; + } } + /* Otherwise authorized and available to activate */ + +out: pending->callback (pending, tmp_error); - nm_auth_chain_unref (chain); g_clear_error (&tmp_error); } @@ -772,12 +785,15 @@ pending_activation_check_authorized (PendingActivation *pending, char *error_desc = NULL; gulong sender_uid = G_MAXULONG; GError *error; + const char *wifi_permission = NULL; + NMConnection *connection; + NMSettings *settings; g_return_if_fail (pending != NULL); g_return_if_fail (dbus_mgr != NULL); if (!nm_auth_get_caller_uid (pending->context, - dbus_mgr, + dbus_mgr, &sender_uid, &error_desc)) { error = g_error_new_literal (NM_MANAGER_ERROR, @@ -795,17 +811,40 @@ pending_activation_check_authorized (PendingActivation *pending, return; } + /* By this point we have an auto-completed connection (for AddAndActivate) + * or an existing connection (for Activate). + */ + connection = pending->connection; + if (!connection) { + settings = NM_MANAGER_GET_PRIVATE (pending->manager)->settings; + connection = (NMConnection *) nm_settings_get_connection_by_path (settings, pending->connection_path); + } + + if (!connection) { + error = g_error_new_literal (NM_MANAGER_ERROR, + NM_MANAGER_ERROR_UNKNOWN_CONNECTION, + "Connection could not be found."); + pending->callback (pending, error); + g_error_free (error); + return; + } + /* First check if the user is allowed to use networking at all, giving * the user a chance to authenticate to gain the permission. */ pending->chain = nm_auth_chain_new (pending->context, NULL, - pending_auth_net_done, + pending_auth_done, pending); g_assert (pending->chain); - nm_auth_chain_add_call (pending->chain, - NM_AUTH_PERMISSION_NETWORK_CONTROL, - TRUE); + nm_auth_chain_add_call (pending->chain, NM_AUTH_PERMISSION_NETWORK_CONTROL, TRUE); + + /* Shared wifi connections require special permissions too */ + wifi_permission = nm_utils_get_shared_wifi_permission (connection); + if (wifi_permission) { + pending->wifi_shared_permission = wifi_permission; + nm_auth_chain_add_call (pending->chain, wifi_permission, TRUE); + } } static void @@ -1040,52 +1079,27 @@ manager_hidden_ap_found (NMDeviceInterface *device, { NMManager *manager = NM_MANAGER (user_data); NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); - const struct ether_addr *ap_addr; - const GByteArray *ap_ssid; + const struct ether_addr *bssid; GSList *iter; GSList *connections; gboolean done = FALSE; - ap_ssid = nm_ap_get_ssid (ap); - if (ap_ssid && ap_ssid->len) - return; + g_return_if_fail (nm_ap_get_ssid (ap) == NULL); - ap_addr = nm_ap_get_address (ap); - g_assert (ap_addr); + bssid = nm_ap_get_address (ap); + g_assert (bssid); /* Look for this AP's BSSID in the seen-bssids list of a connection, * and if a match is found, copy over the SSID */ connections = nm_settings_get_connections (priv->settings); - for (iter = connections; iter && !done; iter = g_slist_next (iter)) { NMConnection *connection = NM_CONNECTION (iter->data); - NMSettingWireless *s_wireless; - const GByteArray *ssid; - guint32 num_bssids; - guint32 i; - - s_wireless = (NMSettingWireless *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS); - if (!s_wireless) - continue; - - num_bssids = nm_setting_wireless_get_num_seen_bssids (s_wireless); - if (num_bssids < 1) - continue; - - ssid = nm_setting_wireless_get_ssid (s_wireless); - g_assert (ssid); + NMSettingWireless *s_wifi; - for (i = 0; i < num_bssids && !done; i++) { - const char *seen_bssid = nm_setting_wireless_get_seen_bssid (s_wireless, i); - struct ether_addr seen_addr; - - if (ether_aton_r (seen_bssid, &seen_addr)) { - if (memcmp (ap_addr, &seen_addr, sizeof (struct ether_addr)) == 0) { - /* Copy the SSID from the connection to the AP */ - nm_ap_set_ssid (ap, ssid); - done = TRUE; - } - } + s_wifi = nm_connection_get_setting_wireless (connection); + if (s_wifi) { + if (nm_settings_connection_has_seen_bssid (NM_SETTINGS_CONNECTION (connection), bssid)) + nm_ap_set_ssid (ap, nm_setting_wireless_get_ssid (s_wifi)); } } g_slist_free (connections); @@ -1667,12 +1681,11 @@ bluez_manager_bdaddr_removed_cb (NMBluezManager *bluez_mgr, g_return_if_fail (bdaddr != NULL); g_return_if_fail (object_path != NULL); - nm_log_info (LOGD_HW, "BT device %s removed", bdaddr); - for (iter = priv->devices; iter; iter = iter->next) { NMDevice *device = NM_DEVICE (iter->data); if (!strcmp (nm_device_get_udi (device), object_path)) { + nm_log_info (LOGD_HW, "BT device %s removed", bdaddr); priv->devices = remove_one_device (self, priv->devices, device, FALSE); break; } @@ -1866,6 +1879,8 @@ internal_activate_device (NMManager *manager, /* Tear down any existing connection */ if (nm_device_get_act_request (device)) { + nm_log_info (LOGD_DEVICE, "(%s): disconnecting for new activation request.", + nm_device_get_iface (device)); nm_device_state_changed (device, NM_DEVICE_STATE_DISCONNECTED, NM_DEVICE_STATE_REASON_NONE); @@ -2040,7 +2055,9 @@ pending_activate (NMManager *self, PendingActivation *pending) if (!path) { nm_log_warn (LOGD_CORE, "connection %s failed to activate: (%d) %s", - pending->connection_path, error->code, error->message); + pending->connection_path, + error ? error->code : -1, + error && error->message ? error->message : "(unknown)"); } else g_object_notify (G_OBJECT (pending->manager), NM_MANAGER_ACTIVE_CONNECTIONS); @@ -3105,6 +3122,13 @@ nm_manager_get (NMSettings *settings, } static void +authority_changed_cb (gpointer user_data) +{ + /* Let clients know they should re-check their authorization */ + g_signal_emit (NM_MANAGER (user_data), signals[CHECK_PERMISSIONS], 0); +} + +static void dispose (GObject *object) { NMManager *manager = NM_MANAGER (object); @@ -3121,7 +3145,7 @@ dispose (GObject *object) g_slist_foreach (priv->auth_chains, (GFunc) nm_auth_chain_unref, NULL); g_slist_free (priv->auth_chains); - nm_auth_set_changed_func (NULL, NULL); + nm_auth_changed_func_unregister (authority_changed_cb, manager); while (g_slist_length (priv->devices)) { priv->devices = remove_one_device (manager, @@ -3398,13 +3422,6 @@ periodic_update_active_connection_timestamps (gpointer user_data) } static void -authority_changed_cb (gpointer user_data) -{ - /* Let clients know they should re-check their authorization */ - g_signal_emit (NM_MANAGER (user_data), signals[CHECK_PERMISSIONS], 0); -} - -static void nm_manager_init (NMManager *manager) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); @@ -3502,7 +3519,7 @@ nm_manager_init (NMManager *manager) nm_log_warn (LOGD_SUSPEND, "could not initialize UPower D-Bus proxy"); /* Listen for authorization changes */ - nm_auth_set_changed_func (authority_changed_cb, manager); + nm_auth_changed_func_register (authority_changed_cb, manager); /* Monitor the firmware directory */ if (strlen (KERNEL_FIRMWARE_DIR)) { diff --git a/src/nm-policy.c b/src/nm-policy.c index 194d1111..a42a0fa9 100644 --- a/src/nm-policy.c +++ b/src/nm-policy.c @@ -46,6 +46,7 @@ #include "nm-dns-manager.h" #include "nm-vpn-manager.h" #include "nm-policy-hostname.h" +#include "nm-manager-auth.h" struct NMPolicy { NMManager *manager; @@ -66,12 +67,17 @@ struct NMPolicy { HostnameThread *lookup; + gint reset_retries_id; /* idle handler for resetting the retries count */ + char *orig_hostname; /* hostname at NM start time */ char *cur_hostname; /* hostname we want to assign */ }; #define RETRIES_TAG "autoconnect-retries" #define RETRIES_DEFAULT 4 +#define RESET_RETRIES_TIMESTAMP_TAG "reset-retries-timestamp-tag" +#define RESET_RETRIES_TIMER 300 +#define FAILURE_REASON_TAG "failure-reason" static NMDevice * get_best_ip4_device (NMManager *manager, NMActRequest **out_req) @@ -316,36 +322,36 @@ update_system_hostname (NMPolicy *policy, NMDevice *best4, NMDevice *best6) /* Grab a hostname out of the device's DHCP4 config */ dhcp4_config = nm_device_get_dhcp4_config (best4); if (dhcp4_config) { - p = dhcp_hostname = nm_dhcp4_config_get_option (dhcp4_config, "host_name"); + p = dhcp_hostname = nm_dhcp4_config_get_option (dhcp4_config, "new_host_name"); if (dhcp_hostname && strlen (dhcp_hostname)) { /* Sanity check; strip leading spaces */ while (*p) { if (!isblank (*p++)) { - _set_hostname (policy, TRUE, dhcp_hostname, "from DHCPv4"); + _set_hostname (policy, TRUE, p-1, "from DHCPv4"); return; } } nm_log_warn (LOGD_DNS, "DHCPv4-provided hostname '%s' looks invalid; ignoring it", - dhcp_hostname); + dhcp_hostname); } } } else if (best6) { NMDHCP6Config *dhcp6_config; - /* Grab a hostname out of the device's DHCP4 config */ + /* Grab a hostname out of the device's DHCP6 config */ dhcp6_config = nm_device_get_dhcp6_config (best6); if (dhcp6_config) { - p = dhcp_hostname = nm_dhcp6_config_get_option (dhcp6_config, "host_name"); + p = dhcp_hostname = nm_dhcp6_config_get_option (dhcp6_config, "new_host_name"); if (dhcp_hostname && strlen (dhcp_hostname)) { /* Sanity check; strip leading spaces */ while (*p) { if (!isblank (*p++)) { - _set_hostname (policy, TRUE, dhcp_hostname, "from DHCPv6"); + _set_hostname (policy, TRUE, p-1, "from DHCPv6"); return; } } nm_log_warn (LOGD_DNS, "DHCPv6-provided hostname '%s' looks invalid; ignoring it", - dhcp_hostname); + dhcp_hostname); } } } @@ -722,16 +728,29 @@ auto_activate_device (gpointer user_data) /* Remove connections that shouldn't be auto-activated */ while (iter) { - NMConnection *candidate = NM_CONNECTION (iter->data); + NMSettingsConnection *candidate = NM_SETTINGS_CONNECTION (iter->data); + gboolean remove_it = FALSE; + const char *permission; /* Grab next item before we possibly delete the current item */ iter = g_slist_next (iter); /* Ignore connections that were tried too many times or are not visible - * to any logged-in users. + * to any logged-in users. Also ignore shared wifi connections for + * which no user has the shared wifi permission. */ - if ( get_connection_auto_retries (candidate) == 0 - || nm_settings_connection_is_visible (NM_SETTINGS_CONNECTION (candidate)) == FALSE) + if ( get_connection_auto_retries (NM_CONNECTION (candidate)) == 0 + || nm_settings_connection_is_visible (candidate) == FALSE) + remove_it = TRUE; + else { + permission = nm_utils_get_shared_wifi_permission (NM_CONNECTION (candidate)); + if (permission) { + if (nm_settings_connection_check_permission (candidate, permission) == FALSE) + remove_it = TRUE; + } + } + + if (remove_it) connections = g_slist_remove (connections, candidate); } @@ -739,6 +758,8 @@ auto_activate_device (gpointer user_data) if (best_connection) { GError *error = NULL; + nm_log_info (LOGD_DEVICE, "Auto-activating connection '%s'.", + nm_connection_get_id (best_connection)); if (!nm_manager_activate_connection (policy->manager, best_connection, specific_object, @@ -817,13 +838,34 @@ hostname_changed (NMManager *manager, GParamSpec *pspec, gpointer user_data) } static void -reset_retries_all (NMSettings *settings) +reset_retries_all (NMSettings *settings, NMDevice *device) +{ + GSList *connections, *iter; + GError *error = NULL; + + connections = nm_settings_get_connections (settings); + for (iter = connections; iter; iter = g_slist_next (iter)) { + if (!device || nm_device_interface_check_connection_compatible (NM_DEVICE_INTERFACE (device), iter->data, &error)) + set_connection_auto_retries (NM_CONNECTION (iter->data), RETRIES_DEFAULT); + g_clear_error (&error); + } + g_slist_free (connections); +} + +static void +reset_retries_for_failed_secrets (NMSettings *settings) { GSList *connections, *iter; connections = nm_settings_get_connections (settings); - for (iter = connections; iter; iter = g_slist_next (iter)) - set_connection_auto_retries (NM_CONNECTION (iter->data), RETRIES_DEFAULT); + for (iter = connections; iter; iter = g_slist_next (iter)) { + NMDeviceStateReason reason = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (iter->data), FAILURE_REASON_TAG)); + + if (reason == NM_DEVICE_STATE_REASON_NO_SECRETS) { + set_connection_auto_retries (NM_CONNECTION (iter->data), RETRIES_DEFAULT); + g_object_set_data (G_OBJECT (iter->data), FAILURE_REASON_TAG, GUINT_TO_POINTER (0)); + } + } g_slist_free (connections); } @@ -838,7 +880,7 @@ sleeping_changed (NMManager *manager, GParamSpec *pspec, gpointer user_data) /* Reset retries on all connections so they'll checked on wakeup */ if (sleeping || !enabled) - reset_retries_all (policy->settings); + reset_retries_all (policy->settings, NULL); } static void @@ -854,6 +896,9 @@ schedule_activate_check (NMPolicy *policy, NMDevice *device, guint delay_seconds if (state < NM_DEVICE_STATE_DISCONNECTED) return; + if (!nm_device_interface_get_enabled (NM_DEVICE_INTERFACE (device))) + return; + if (!nm_device_autoconnect_allowed (device)) return; @@ -864,6 +909,37 @@ schedule_activate_check (NMPolicy *policy, NMDevice *device, guint delay_seconds } } +static gboolean +reset_connections_retries (gpointer user_data) +{ + NMPolicy *policy = (NMPolicy *) user_data; + GSList *connections, *iter; + time_t con_stamp, min_stamp, now; + + policy->reset_retries_id = 0; + + min_stamp = now = time (NULL); + connections = nm_settings_get_connections (policy->settings); + for (iter = connections; iter; iter = g_slist_next (iter)) { + con_stamp = GPOINTER_TO_SIZE (g_object_get_data (G_OBJECT (iter->data), RESET_RETRIES_TIMESTAMP_TAG)); + if (con_stamp == 0) + continue; + if (con_stamp + RESET_RETRIES_TIMER <= now) { + set_connection_auto_retries (NM_CONNECTION (iter->data), RETRIES_DEFAULT); + g_object_set_data (G_OBJECT (iter->data), RESET_RETRIES_TIMESTAMP_TAG, GSIZE_TO_POINTER (0)); + continue; + } + if (con_stamp < min_stamp) + min_stamp = con_stamp; + } + g_slist_free (connections); + + /* Schedule the handler again if there are some stamps left */ + if (min_stamp != now) + policy->reset_retries_id = g_timeout_add_seconds (RESET_RETRIES_TIMER - (now - min_stamp), reset_connections_retries, policy); + return FALSE; +} + static NMConnection * get_device_connection (NMDevice *device) { @@ -886,6 +962,9 @@ device_state_changed (NMDevice *device, NMPolicy *policy = (NMPolicy *) user_data; NMConnection *connection = get_device_connection (device); + if (connection) + g_object_set_data (G_OBJECT (connection), FAILURE_REASON_TAG, GUINT_TO_POINTER (0)); + switch (new_state) { case NM_DEVICE_STATE_FAILED: /* Mark the connection invalid if it failed during activation so that @@ -900,6 +979,11 @@ device_state_changed (NMDevice *device, * automatically retrying because it's just going to fail anyway. */ set_connection_auto_retries (connection, 0); + + /* Mark the connection as failed due to missing secrets so that we can reset + * RETRIES_TAG and automatically re-try when an secret agent registers. + */ + g_object_set_data (G_OBJECT (connection), FAILURE_REASON_TAG, GUINT_TO_POINTER (NM_DEVICE_STATE_REASON_NO_SECRETS)); } else if (tries > 0) { /* Otherwise if it's a random failure, just decrease the number * of automatic retries so that the connection gets tried again @@ -908,8 +992,13 @@ device_state_changed (NMDevice *device, set_connection_auto_retries (connection, tries - 1); } - if (get_connection_auto_retries (connection) == 0) + if (get_connection_auto_retries (connection) == 0) { nm_log_info (LOGD_DEVICE, "Marking connection '%s' invalid.", nm_connection_get_id (connection)); + /* Schedule a handler to reset retries count */ + g_object_set_data (G_OBJECT (connection), RESET_RETRIES_TIMESTAMP_TAG, GSIZE_TO_POINTER ((gsize) time (NULL))); + if (!policy->reset_retries_id) + policy->reset_retries_id = g_timeout_add_seconds (RESET_RETRIES_TIMER, reset_connections_retries, policy); + } nm_connection_clear_secrets (connection); } schedule_activate_check (policy, device, 3); @@ -932,6 +1021,11 @@ device_state_changed (NMDevice *device, update_routing_and_dns (policy, FALSE); break; case NM_DEVICE_STATE_DISCONNECTED: + /* Reset RETRIES_TAG when carrier on. If cable was unplugged + * and plugged again, we should try to reconnect */ + if (reason == NM_DEVICE_STATE_REASON_CARRIER && old_state == NM_DEVICE_STATE_UNAVAILABLE) + reset_retries_all (policy->settings, device); + /* Device is now available for auto-activation */ update_routing_and_dns (policy, FALSE); schedule_activate_check (policy, device, 0); @@ -963,6 +1057,12 @@ nsps_changed (NMDeviceWimax *device, NMWimaxNsp *nsp, gpointer user_data) } #endif +static void +modem_enabled_changed (NMDeviceModem *device, gpointer user_data) +{ + schedule_activate_check ((NMPolicy *) (user_data), NM_DEVICE (device), 0); +} + typedef struct { gulong id; NMDevice *device; @@ -997,6 +1097,8 @@ device_added (NMManager *manager, NMDevice *device, gpointer user_data) _connect_device_signal (policy, device, "nsp-added", nsps_changed); _connect_device_signal (policy, device, "nsp-removed", nsps_changed); #endif + } else if (NM_IS_DEVICE_MODEM (device)) { + _connect_device_signal (policy, device, NM_DEVICE_MODEM_ENABLE_CHANGED, modem_enabled_changed); } } @@ -1058,7 +1160,7 @@ connections_loaded (NMSettings *settings, gpointer user_data) // that by calling reset_retries_all() in nm_policy_new() /* Initialize connections' auto-retries */ - reset_retries_all (settings); + reset_retries_all (settings, NULL); schedule_activate_all ((NMPolicy *) user_data); } @@ -1122,6 +1224,19 @@ connection_visibility_changed (NMSettings *settings, } static void +secret_agent_registered (NMSettings *settings, + NMSecretAgent *agent, + gpointer user_data) +{ + /* The registered secret agent may provide some missing secrets. Thus we + * reset retries count here and schedule activation, so that the + * connections failed due to missing secrets may re-try auto-connection. + */ + reset_retries_for_failed_secrets (settings); + schedule_activate_all ((NMPolicy *) user_data); +} + +static void _connect_manager_signal (NMPolicy *policy, const char *name, gpointer callback) { guint id; @@ -1161,7 +1276,10 @@ nm_policy_new (NMManager *manager, memset (hostname, 0, sizeof (hostname)); if (gethostname (&hostname[0], HOST_NAME_MAX) == 0) { /* only cache it if it's a valid hostname */ - if (strlen (hostname) && strcmp (hostname, "localhost") && strcmp (hostname, "localhost.localdomain")) + if ( strlen (hostname) + && strcmp (hostname, "localhost") + && strcmp (hostname, "localhost.localdomain") + && strcmp (hostname, "(none)")) policy->orig_hostname = g_strdup (hostname); } @@ -1186,9 +1304,10 @@ nm_policy_new (NMManager *manager, _connect_settings_signal (policy, NM_SETTINGS_SIGNAL_CONNECTION_REMOVED, connection_removed); _connect_settings_signal (policy, NM_SETTINGS_SIGNAL_CONNECTION_VISIBILITY_CHANGED, connection_visibility_changed); + _connect_settings_signal (policy, NM_SETTINGS_SIGNAL_AGENT_REGISTERED, secret_agent_registered); /* Initialize connections' auto-retries */ - reset_retries_all (policy->settings); + reset_retries_all (policy->settings, NULL); initialized = TRUE; return policy; @@ -1232,6 +1351,9 @@ nm_policy_destroy (NMPolicy *policy) } g_slist_free (policy->dev_ids); + if (policy->reset_retries_id) + g_source_remove (policy->reset_retries_id); + g_free (policy->orig_hostname); g_free (policy->cur_hostname); diff --git a/src/nm-udev-manager.c b/src/nm-udev-manager.c index e0795e81..72501c28 100644 --- a/src/nm-udev-manager.c +++ b/src/nm-udev-manager.c @@ -384,6 +384,9 @@ is_wireless (GUdevDevice *device) g_assert (ifname); fd = socket (PF_INET, SOCK_DGRAM, 0); + if (fd == -1) + return FALSE; + strncpy (iwr.ifr_ifrn.ifrn_name, ifname, IFNAMSIZ); path = g_udev_device_get_sysfs_path (device); diff --git a/src/settings/nm-agent-manager.c b/src/settings/nm-agent-manager.c index 8a5ea106..5ccbdc67 100644 --- a/src/settings/nm-agent-manager.c +++ b/src/settings/nm-agent-manager.c @@ -49,6 +49,9 @@ typedef struct { NMDBusManager *dbus_mgr; NMSessionMonitor *session_monitor; + /* Auth chains for checking agent permissions */ + GSList *chains; + /* Hashed by owner name, not identifier, since two agents in different * sessions can use the same identifier. */ @@ -57,6 +60,14 @@ typedef struct { GHashTable *requests; } NMAgentManagerPrivate; +enum { + AGENT_REGISTERED, + + LAST_SIGNAL +}; +static guint signals[LAST_SIGNAL] = { 0 }; + + typedef struct _Request Request; static void request_add_agent (Request *req, @@ -218,6 +229,59 @@ validate_identifier (const char *identifier, GError **error) } static void +agent_register_permissions_done (NMAuthChain *chain, + GError *error, + DBusGMethodInvocation *context, + gpointer user_data) +{ + NMAgentManager *self = NM_AGENT_MANAGER (user_data); + NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (self); + NMSecretAgent *agent; + const char *sender; + GError *local = NULL; + NMAuthCallResult result; + GHashTableIter iter; + Request *req; + + priv->chains = g_slist_remove (priv->chains, chain); + + if (error) { + local = g_error_new (NM_AGENT_MANAGER_ERROR, + NM_AGENT_MANAGER_ERROR_PERMISSION_DENIED, + "Failed to request agent permissions: (%d) %s", + error->code, error->message); + dbus_g_method_return_error (context, local); + g_error_free (local); + } else { + agent = nm_auth_chain_steal_data (chain, "agent"); + + result = nm_auth_chain_get_result (chain, NM_AUTH_PERMISSION_WIFI_SHARE_PROTECTED); + if (result == NM_AUTH_CALL_RESULT_YES) + nm_secret_agent_add_permission (agent, NM_AUTH_PERMISSION_WIFI_SHARE_PROTECTED, TRUE); + + result = nm_auth_chain_get_result (chain, NM_AUTH_PERMISSION_WIFI_SHARE_OPEN); + if (result == NM_AUTH_CALL_RESULT_YES) + nm_secret_agent_add_permission (agent, NM_AUTH_PERMISSION_WIFI_SHARE_OPEN, TRUE); + + sender = nm_secret_agent_get_dbus_owner (agent); + g_hash_table_insert (priv->agents, g_strdup (sender), agent); + nm_log_dbg (LOGD_AGENTS, "(%s) agent registered", + nm_secret_agent_get_description (agent)); + dbus_g_method_return (context); + + /* Signal an agent was registered */ + g_signal_emit (self, signals[AGENT_REGISTERED], 0, agent); + + /* Add this agent to any in-progress secrets requests */ + g_hash_table_iter_init (&iter, priv->requests); + while (g_hash_table_iter_next (&iter, NULL, (gpointer) &req)) + request_add_agent (req, agent, priv->session_monitor); + } + + nm_auth_chain_unref (chain); +} + +static void impl_agent_manager_register (NMAgentManager *self, const char *identifier, DBusGMethodInvocation *context) @@ -227,8 +291,7 @@ impl_agent_manager_register (NMAgentManager *self, gulong sender_uid = G_MAXULONG; GError *error = NULL, *local = NULL; NMSecretAgent *agent; - GHashTableIter iter; - gpointer data; + NMAuthChain *chain; if (!nm_auth_get_caller_uid (context, priv->dbus_mgr, @@ -272,15 +335,16 @@ impl_agent_manager_register (NMAgentManager *self, goto done; } - g_hash_table_insert (priv->agents, g_strdup (sender), agent); - nm_log_dbg (LOGD_AGENTS, "(%s) agent registered", + nm_log_dbg (LOGD_AGENTS, "(%s) requesting permissions", nm_secret_agent_get_description (agent)); - dbus_g_method_return (context); - /* Add this agent to any in-progress secrets requests */ - g_hash_table_iter_init (&iter, priv->requests); - while (g_hash_table_iter_next (&iter, NULL, &data)) - request_add_agent ((Request *) data, agent, priv->session_monitor); + /* Kick off permissions requests for this agent */ + chain = nm_auth_chain_new (context, NULL, agent_register_permissions_done, self); + nm_auth_chain_set_data (chain, "agent", agent, g_object_unref); + nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_WIFI_SHARE_PROTECTED, FALSE); + nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_WIFI_SHARE_OPEN, FALSE); + + priv->chains = g_slist_append (priv->chains, chain); done: if (error) @@ -945,8 +1009,8 @@ get_start (gpointer user_data) g_clear_error (&error); } else { /* Do we have everything we need? */ - /* FIXME: handle second check for VPN connections */ - if ((nm_connection_need_secrets (tmp, NULL) == NULL) && (request_new == FALSE)) { + if ( (req->flags & NM_SETTINGS_GET_SECRETS_FLAG_ONLY_SYSTEM) + || ((nm_connection_need_secrets (tmp, NULL) == NULL) && (request_new == FALSE))) { nm_log_dbg (LOGD_AGENTS, "(%p/%s) system settings secrets sufficient", req, req->setting_name); @@ -1059,7 +1123,8 @@ nm_agent_manager_get_secrets (NMAgentManager *self, g_hash_table_insert (priv->requests, GUINT_TO_POINTER (req->reqid), req); /* Kick off the request */ - request_add_agents (self, req); + if (!(req->flags & NM_SETTINGS_GET_SECRETS_FLAG_ONLY_SYSTEM)) + request_add_agents (self, req); req->idle_id = g_idle_add (get_start, req); return req->reqid; @@ -1277,6 +1342,24 @@ nm_agent_manager_delete_secrets (NMAgentManager *self, /*************************************************************/ +NMSecretAgent * +nm_agent_manager_get_agent_by_user (NMAgentManager *self, const char *username) +{ + NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (self); + GHashTableIter iter; + NMSecretAgent *agent; + + g_hash_table_iter_init (&iter, priv->agents); + while (g_hash_table_iter_next (&iter, NULL, (gpointer) &agent)) { + if (g_strcmp0 (nm_secret_agent_get_owner_username (agent), username) == 0) + return agent; + } + + return NULL; +} + +/*************************************************************/ + static void name_owner_changed_cb (NMDBusManager *dbus_mgr, const char *name, @@ -1290,6 +1373,73 @@ name_owner_changed_cb (NMDBusManager *dbus_mgr, } } +static void +agent_permissions_changed_done (NMAuthChain *chain, + GError *error, + DBusGMethodInvocation *context, + gpointer user_data) +{ + NMAgentManager *self = NM_AGENT_MANAGER (user_data); + NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (self); + NMSecretAgent *agent; + NMAuthCallResult result; + + priv->chains = g_slist_remove (priv->chains, chain); + + agent = nm_auth_chain_get_data (chain, "agent"); + + if (error) { + nm_log_dbg (LOGD_AGENTS, "(%s) failed to request updated agent permissions", + nm_secret_agent_get_description (agent)); + nm_secret_agent_add_permission (agent, NM_AUTH_PERMISSION_WIFI_SHARE_PROTECTED, FALSE); + nm_secret_agent_add_permission (agent, NM_AUTH_PERMISSION_WIFI_SHARE_OPEN, FALSE); + } else { + nm_log_dbg (LOGD_AGENTS, "(%s) updated agent permissions", + nm_secret_agent_get_description (agent)); + + result = nm_auth_chain_get_result (chain, NM_AUTH_PERMISSION_WIFI_SHARE_PROTECTED); + nm_secret_agent_add_permission (agent, + NM_AUTH_PERMISSION_WIFI_SHARE_PROTECTED, + (result == NM_AUTH_CALL_RESULT_YES)); + + result = nm_auth_chain_get_result (chain, NM_AUTH_PERMISSION_WIFI_SHARE_OPEN); + nm_secret_agent_add_permission (agent, + NM_AUTH_PERMISSION_WIFI_SHARE_OPEN, + (result == NM_AUTH_CALL_RESULT_YES)); + } + + nm_auth_chain_unref (chain); +} + +static void +authority_changed_cb (gpointer user_data) +{ + NMAgentManager *self = NM_AGENT_MANAGER (user_data); + NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (self); + GHashTableIter iter; + NMSecretAgent *agent; + + /* Recheck the permissions of all secret agents */ + g_hash_table_iter_init (&iter, priv->agents); + while (g_hash_table_iter_next (&iter, NULL, (gpointer) &agent)) { + NMAuthChain *chain; + const char *sender; + + /* Kick off permissions requests for this agent */ + sender = nm_secret_agent_get_dbus_owner (agent); + chain = nm_auth_chain_new_dbus_sender (sender, agent_permissions_changed_done, self); + + /* Make sure if the agent quits while the permissions call is in progress + * that the object sticks around until our callback. + */ + nm_auth_chain_set_data (chain, "agent", g_object_ref (agent), g_object_unref); + nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_WIFI_SHARE_PROTECTED, FALSE); + nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_WIFI_SHARE_OPEN, FALSE); + + priv->chains = g_slist_append (priv->chains, chain); + } +} + /*************************************************************/ NMAgentManager * @@ -1319,6 +1469,8 @@ nm_agent_manager_get (void) G_CALLBACK (name_owner_changed_cb), singleton); + nm_auth_changed_func_register (authority_changed_cb, singleton); + return singleton; } @@ -1342,6 +1494,10 @@ dispose (GObject *object) if (!priv->disposed) { priv->disposed = TRUE; + nm_auth_changed_func_unregister (authority_changed_cb, NM_AGENT_MANAGER (object)); + + g_slist_foreach (priv->chains, (GFunc) nm_auth_chain_unref, NULL); + g_hash_table_destroy (priv->agents); g_hash_table_destroy (priv->requests); @@ -1362,6 +1518,17 @@ nm_agent_manager_class_init (NMAgentManagerClass *agent_manager_class) /* virtual methods */ object_class->dispose = dispose; + /* Signals */ + signals[AGENT_REGISTERED] = + g_signal_new ("agent-registered", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMAgentManagerClass, agent_registered), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + G_TYPE_OBJECT); + dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (agent_manager_class), &dbus_glib_nm_agent_manager_object_info); diff --git a/src/settings/nm-agent-manager.h b/src/settings/nm-agent-manager.h index 788a9175..e49f579d 100644 --- a/src/settings/nm-agent-manager.h +++ b/src/settings/nm-agent-manager.h @@ -25,6 +25,7 @@ #include <glib-object.h> #include <nm-connection.h> #include "nm-settings-flags.h" +#include "nm-secret-agent.h" #define NM_TYPE_AGENT_MANAGER (nm_agent_manager_get_type ()) #define NM_AGENT_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_AGENT_MANAGER, NMAgentManager)) @@ -39,6 +40,9 @@ typedef struct { typedef struct { GObjectClass parent; + + /* Signals */ + void (*agent_registered) (NMAgentManager *agent_mgr, NMSecretAgent *agent); } NMAgentManagerClass; GType nm_agent_manager_get_type (void); @@ -85,4 +89,7 @@ guint32 nm_agent_manager_delete_secrets (NMAgentManager *manager, gboolean filter_by_uid, gulong uid_filter); +NMSecretAgent *nm_agent_manager_get_agent_by_user (NMAgentManager *manager, + const char *username); + #endif /* NM_AGENT_MANAGER_H */ diff --git a/src/settings/nm-secret-agent.c b/src/settings/nm-secret-agent.c index 2b1156cb..94f046e5 100644 --- a/src/settings/nm-secret-agent.c +++ b/src/settings/nm-secret-agent.c @@ -20,6 +20,9 @@ #include <config.h> +#include <sys/types.h> +#include <pwd.h> + #include <glib.h> #include <dbus/dbus-glib.h> #include <dbus/dbus-glib-lowlevel.h> @@ -41,8 +44,11 @@ typedef struct { char *owner; char *identifier; uid_t owner_uid; + char *owner_username; guint32 hash; + GSList *permissions; + NMDBusManager *dbus_mgr; DBusGProxy *proxy; @@ -134,6 +140,15 @@ nm_secret_agent_get_owner_uid (NMSecretAgent *agent) return NM_SECRET_AGENT_GET_PRIVATE (agent)->owner_uid; } +const char * +nm_secret_agent_get_owner_username(NMSecretAgent *agent) +{ + g_return_val_if_fail (agent != NULL, NULL); + g_return_val_if_fail (NM_IS_SECRET_AGENT (agent), NULL); + + return NM_SECRET_AGENT_GET_PRIVATE (agent)->owner_username; +} + guint32 nm_secret_agent_get_hash (NMSecretAgent *agent) { @@ -143,6 +158,76 @@ nm_secret_agent_get_hash (NMSecretAgent *agent) return NM_SECRET_AGENT_GET_PRIVATE (agent)->hash; } +/** + * nm_secret_agent_add_permission: + * @agent: A #NMSecretAgent. + * @permission: The name of the permission + * + * Records whether or not the agent has a given permission. + */ +void +nm_secret_agent_add_permission (NMSecretAgent *agent, + const char *permission, + gboolean allowed) +{ + NMSecretAgentPrivate *priv; + GSList *iter; + + g_return_if_fail (agent != NULL); + g_return_if_fail (permission != NULL); + + priv = NM_SECRET_AGENT_GET_PRIVATE (agent); + + /* Check if the permission is already in the list */ + for (iter = priv->permissions; iter; iter = g_slist_next (iter)) { + if (g_strcmp0 (permission, iter->data) == 0) { + /* If the permission is no longer allowed, remove it from the + * list. If it is now allowed, do nothing since it's already + * in the list. + */ + if (allowed == FALSE) { + g_free (iter->data); + priv->permissions = g_slist_delete_link (priv->permissions, iter); + } + return; + } + } + + /* New permission that's allowed */ + if (allowed) + priv->permissions = g_slist_prepend (priv->permissions, g_strdup (permission)); +} + +/** + * nm_secret_agent_has_permission: + * @agent: A #NMSecretAgent. + * @permission: The name of the permission to check for + * + * Returns whether or not the agent has the given permission. + * + * Returns: %TRUE if the agent has the given permission, %FALSE if it does not + * or if the permission was not previous recorded with + * nm_secret_agent_add_permission(). + */ +gboolean +nm_secret_agent_has_permission (NMSecretAgent *agent, const char *permission) +{ + NMSecretAgentPrivate *priv; + GSList *iter; + + g_return_val_if_fail (agent != NULL, FALSE); + g_return_val_if_fail (permission != NULL, FALSE); + + priv = NM_SECRET_AGENT_GET_PRIVATE (agent); + + /* Check if the permission is already in the list */ + for (iter = priv->permissions; iter; iter = g_slist_next (iter)) { + if (g_strcmp0 (permission, iter->data) == 0) + return TRUE; + } + return FALSE; +} + /*************************************************************/ static void @@ -189,6 +274,9 @@ nm_secret_agent_get_secrets (NMSecretAgent *self, hash = nm_connection_to_hash (connection, NM_SETTING_HASH_FLAG_ALL); + /* Mask off the private ONLY_SYSTEM flag if present */ + flags &= ~NM_SETTINGS_GET_SECRETS_FLAG_ONLY_SYSTEM; + r = request_new (self, nm_connection_get_path (connection), setting_name, callback, callback_data); r->call = dbus_g_proxy_begin_call_with_timeout (priv->proxy, "GetSecrets", @@ -327,11 +415,17 @@ nm_secret_agent_new (NMDBusManager *dbus_mgr, NMSecretAgent *self; NMSecretAgentPrivate *priv; DBusGConnection *bus; - char *hash_str; + char *hash_str, *username; + struct passwd *pw; g_return_val_if_fail (owner != NULL, NULL); g_return_val_if_fail (identifier != NULL, NULL); + pw = getpwuid (owner_uid); + g_return_val_if_fail (pw != NULL, NULL); + g_return_val_if_fail (pw->pw_name[0] != '\0', NULL); + username = g_strdup (pw->pw_name); + self = (NMSecretAgent *) g_object_new (NM_TYPE_SECRET_AGENT, NULL); if (self) { priv = NM_SECRET_AGENT_GET_PRIVATE (self); @@ -339,6 +433,7 @@ nm_secret_agent_new (NMDBusManager *dbus_mgr, priv->owner = g_strdup (owner); priv->identifier = g_strdup (identifier); priv->owner_uid = owner_uid; + priv->owner_username = g_strdup (username); hash_str = g_strdup_printf ("%08u%s", owner_uid, identifier); priv->hash = g_str_hash (hash_str); @@ -353,6 +448,7 @@ nm_secret_agent_new (NMDBusManager *dbus_mgr, g_assert (priv->proxy); } + g_free (username); return self; } @@ -376,6 +472,10 @@ dispose (GObject *object) g_free (priv->description); g_free (priv->owner); g_free (priv->identifier); + g_free (priv->owner_username); + + g_slist_foreach (priv->permissions, (GFunc) g_free, NULL); + g_slist_free (priv->permissions); g_hash_table_destroy (priv->requests); g_object_unref (priv->proxy); diff --git a/src/settings/nm-secret-agent.h b/src/settings/nm-secret-agent.h index 597940b4..29b02be1 100644 --- a/src/settings/nm-secret-agent.h +++ b/src/settings/nm-secret-agent.h @@ -60,8 +60,17 @@ const char *nm_secret_agent_get_identifier (NMSecretAgent *agent); uid_t nm_secret_agent_get_owner_uid (NMSecretAgent *agent); +const char *nm_secret_agent_get_owner_username (NMSecretAgent *agent); + guint32 nm_secret_agent_get_hash (NMSecretAgent *agent); +void nm_secret_agent_add_permission (NMSecretAgent *agent, + const char *permission, + gboolean allowed); + +gboolean nm_secret_agent_has_permission (NMSecretAgent *agent, + const char *permission); + typedef void (*NMSecretAgentCallback) (NMSecretAgent *agent, gconstpointer call, GHashTable *new_secrets, /* NULL for save & delete */ diff --git a/src/settings/nm-settings-connection.c b/src/settings/nm-settings-connection.c index 60de6b06..4b3a56cd 100644 --- a/src/settings/nm-settings-connection.c +++ b/src/settings/nm-settings-connection.c @@ -22,6 +22,7 @@ #include "config.h" #include <string.h> +#include <netinet/ether.h> #include <NetworkManager.h> #include <dbus/dbus-glib-lowlevel.h> @@ -38,8 +39,10 @@ #include "nm-manager-auth.h" #include "nm-marshal.h" #include "nm-agent-manager.h" +#include "NetworkManagerUtils.h" #define SETTINGS_TIMESTAMPS_FILE LOCALSTATEDIR"/lib/NetworkManager/timestamps" +#define SETTINGS_SEEN_BSSIDS_FILE LOCALSTATEDIR"/lib/NetworkManager/seen-bssids" static void impl_settings_connection_get_settings (NMSettingsConnection *connection, DBusGMethodInvocation *context); @@ -91,7 +94,8 @@ typedef struct { NMSessionMonitor *session_monitor; guint session_changed_id; - guint64 timestamp; /* Up-to-date timestamp of connection use */ + guint64 timestamp; /* Up-to-date timestamp of connection use */ + GHashTable *seen_bssids; /* Up-to-date BSSIDs that's been seen for the connection */ } NMSettingsConnectionPrivate; /**************************************************************/ @@ -238,6 +242,58 @@ session_changed_cb (NMSessionMonitor *self, gpointer user_data) /**************************************************************/ +/* Return TRUE if any active user in the connection's ACL has the given + * permission without having to authorize for it via PolicyKit. Connections + * visible to everyone automatically pass the check. + */ +gboolean +nm_settings_connection_check_permission (NMSettingsConnection *self, + const char *permission) +{ + NMSettingsConnectionPrivate *priv; + NMSettingConnection *s_con; + guint32 num, i; + const char *puser; + + g_return_val_if_fail (self != NULL, FALSE); + g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), FALSE); + + priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); + + if (priv->visible == FALSE) + return FALSE; + + s_con = nm_connection_get_setting_connection (NM_CONNECTION (self)); + g_assert (s_con); + + /* Check every user in the ACL for a session */ + num = nm_setting_connection_get_num_permissions (s_con); + if (num == 0) { + /* Visible to all so it's OK to auto-activate */ + return TRUE; + } + + for (i = 0; i < num; i++) { + /* For each user get their secret agent and check if that agent has the + * required permission. + * + * FIXME: what if the user isn't running an agent? PolKit needs a bus + * name or a PID but if the user isn't running an agent they won't have + * either. + */ + if (nm_setting_connection_get_permission (s_con, i, NULL, &puser, NULL)) { + NMSecretAgent *agent = nm_agent_manager_get_agent_by_user (priv->agent_mgr, puser); + + if (agent && nm_secret_agent_has_permission (agent, permission)) + return TRUE; + } + } + + return FALSE; +} + +/**************************************************************/ + static void only_system_secrets_cb (NMSetting *setting, const char *key, @@ -326,25 +382,14 @@ nm_settings_connection_replace_settings (NMSettingsConnection *self, new_settings = nm_connection_to_hash (new, NM_SETTING_HASH_FLAG_ALL); g_assert (new_settings); if (nm_connection_replace_settings (NM_CONNECTION (self), new_settings, error)) { - GHashTableIter iter; - NMSetting *setting; - const char *setting_name; - GHashTable *setting_hash; - /* Copy the connection to keep its secrets around even if NM * calls nm_connection_clear_secrets(). */ update_secrets_cache (self); /* And add the transient secrets back */ - if (transient_secrets) { - g_hash_table_iter_init (&iter, transient_secrets); - while (g_hash_table_iter_next (&iter, (gpointer) &setting_name, (gpointer) &setting_hash)) { - setting = nm_connection_get_setting_by_name (NM_CONNECTION (self), setting_name); - if (setting) - nm_setting_update_secrets (setting, setting_hash, NULL); - } - } + if (transient_secrets) + nm_connection_update_secrets (NM_CONNECTION (self), NULL, transient_secrets, NULL); nm_settings_connection_recheck_visibility (self); success = TRUE; @@ -455,12 +500,20 @@ commit_changes (NMSettingsConnection *connection, } static void -remove_timestamp_from_db (NMSettingsConnection *connection) +remove_entry_from_db (NMSettingsConnection *connection, const char* db_name) { - GKeyFile *timestamps_file; + GKeyFile *key_file; + const char *db_file; - timestamps_file = g_key_file_new (); - if (g_key_file_load_from_file (timestamps_file, SETTINGS_TIMESTAMPS_FILE, G_KEY_FILE_KEEP_COMMENTS, NULL)) { + if (strcmp (db_name, "timestamps") == 0) + db_file = SETTINGS_TIMESTAMPS_FILE; + else if (strcmp (db_name, "seen-bssids") == 0) + db_file = SETTINGS_SEEN_BSSIDS_FILE; + else + return; + + key_file = g_key_file_new (); + if (g_key_file_load_from_file (key_file, db_file, G_KEY_FILE_KEEP_COMMENTS, NULL)) { const char *connection_uuid; char *data; gsize len; @@ -468,18 +521,18 @@ remove_timestamp_from_db (NMSettingsConnection *connection) connection_uuid = nm_connection_get_uuid (NM_CONNECTION (connection)); - g_key_file_remove_key (timestamps_file, "timestamps", connection_uuid, NULL); - data = g_key_file_to_data (timestamps_file, &len, &error); + g_key_file_remove_key (key_file, db_name, connection_uuid, NULL); + data = g_key_file_to_data (key_file, &len, &error); if (data) { - g_file_set_contents (SETTINGS_TIMESTAMPS_FILE, data, len, &error); + g_file_set_contents (db_file, data, len, &error); g_free (data); } if (error) { - nm_log_warn (LOGD_SETTINGS, "error writing timestamps file '%s': %s", SETTINGS_TIMESTAMPS_FILE, error->message); + nm_log_warn (LOGD_SETTINGS, "error writing %s file '%s': %s", db_name, db_file, error->message); g_error_free (error); } } - g_key_file_free (timestamps_file); + g_key_file_free (key_file); } static void @@ -499,7 +552,10 @@ do_delete (NMSettingsConnection *connection, nm_agent_manager_delete_secrets (priv->agent_mgr, for_agents, FALSE, 0); /* Remove timestamp from timestamps database file */ - remove_timestamp_from_db (connection); + remove_entry_from_db (connection, "timestamps"); + + /* Remove connection from seen-bssids database file */ + remove_entry_from_db (connection, "seen-bssids"); /* Signal the connection is removed and deleted */ g_signal_emit (connection, signals[REMOVED], 0); @@ -980,21 +1036,21 @@ check_writable (NMConnection *connection, GError **error) static void get_settings_auth_cb (NMSettingsConnection *self, - DBusGMethodInvocation *context, - gulong sender_uid, - GError *error, - gpointer data) + DBusGMethodInvocation *context, + gulong sender_uid, + GError *error, + gpointer data) { if (error) dbus_g_method_return_error (context, error); else { GHashTable *settings; - NMConnection *dupl_con; + NMConnection *dupl_con; NMSettingConnection *s_con; guint64 timestamp; - dupl_con = nm_connection_duplicate (NM_CONNECTION (self)); - g_assert (dupl_con); + dupl_con = nm_connection_duplicate (NM_CONNECTION (self)); + g_assert (dupl_con); /* Timestamp is not updated in connection's 'timestamp' property, * because it would force updating the connection and in turn @@ -1004,7 +1060,7 @@ get_settings_auth_cb (NMSettingsConnection *self, */ timestamp = nm_settings_connection_get_timestamp (self); if (timestamp) { - s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (NM_CONNECTION (dupl_con), NM_TYPE_SETTING_CONNECTION)); + s_con = nm_connection_get_setting_connection (NM_CONNECTION (dupl_con)); g_assert (s_con); g_object_set (s_con, NM_SETTING_CONNECTION_TIMESTAMP, timestamp, NULL); } @@ -1017,7 +1073,7 @@ get_settings_auth_cb (NMSettingsConnection *self, g_assert (settings); dbus_g_method_return (context, settings); g_hash_table_destroy (settings); - g_object_unref (dupl_con); + g_object_unref (dupl_con); } } @@ -1042,31 +1098,30 @@ con_update_cb (NMSettingsConnection *connection, } static void -only_agent_secrets_cb (NMSetting *setting, - const char *key, - const GValue *value, - GParamFlags flags, - gpointer user_data) +secrets_filter_cb (NMSetting *setting, + const char *key, + const GValue *value, + GParamFlags flags, + gpointer user_data) { - if (flags & NM_SETTING_PARAM_SECRET) { - NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE; + NMSettingSecretFlags filter_flags = GPOINTER_TO_UINT (user_data); + NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE; + const char *secret_name = NULL; + GHashTableIter iter; - /* Clear out system-owned or always-ask secrets */ + if (flags & NM_SETTING_PARAM_SECRET) { if (NM_IS_SETTING_VPN (setting) && !strcmp (key, NM_SETTING_VPN_SECRETS)) { - GHashTableIter iter; - const char *secret_name = NULL; - /* VPNs are special; need to handle each secret separately */ g_hash_table_iter_init (&iter, (GHashTable *) g_value_get_boxed (value)); - while (g_hash_table_iter_next (&iter, (gpointer *) &secret_name, NULL)) { + while (g_hash_table_iter_next (&iter, (gpointer) &secret_name, NULL)) { secret_flags = NM_SETTING_SECRET_FLAG_NONE; nm_setting_get_secret_flags (setting, secret_name, &secret_flags, NULL); - if (secret_flags != NM_SETTING_SECRET_FLAG_AGENT_OWNED) + if (!(secret_flags & filter_flags)) nm_setting_vpn_remove_secret (NM_SETTING_VPN (setting), secret_name); } } else { nm_setting_get_secret_flags (setting, key, &secret_flags, NULL); - if (secret_flags != NM_SETTING_SECRET_FLAG_AGENT_OWNED) + if (!(secret_flags & filter_flags)) g_object_set (G_OBJECT (setting), key, NULL, NULL); } } @@ -1081,23 +1136,52 @@ update_auth_cb (NMSettingsConnection *self, { NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); NMConnection *new_settings = data; - NMConnection *for_agent; + NMConnection *for_agent, *dup; + NMSettingSecretFlags filter_flags; + GHashTable *hash; + GError *local = NULL; if (error) dbus_g_method_return_error (context, error); else { + /* Cache the new secrets since they may get overwritten by the replace + * when transient secrets are copied back. + */ + dup = nm_connection_duplicate (new_settings); + /* Update and commit our settings. */ nm_settings_connection_replace_and_commit (self, new_settings, con_update_cb, context); + /* Copy new agent secrets back to the connection */ + filter_flags = NM_SETTING_SECRET_FLAG_AGENT_OWNED | NM_SETTING_SECRET_FLAG_NOT_SAVED; + nm_connection_for_each_setting_value (dup, + secrets_filter_cb, + GUINT_TO_POINTER (filter_flags)); + hash = nm_connection_to_hash (dup, NM_SETTING_HASH_FLAG_ONLY_SECRETS); + g_object_unref (dup); + + if (hash) { + if (!nm_connection_update_secrets (NM_CONNECTION (self), NULL, hash, &local)) { + nm_log_warn (LOGD_SETTINGS, "Failed to update connection secrets: (%d) %s", + local ? local->code : -1, + local && local->message ? local->message : "(unknown)"); + g_clear_error (&local); + } + g_hash_table_destroy (hash); + } + /* Dupe the connection and clear out non-agent-owned secrets so we can * send the agent-owned ones to agents to be saved. Only send them to * agents of the same UID as the Update() request sender. */ for_agent = nm_connection_duplicate (NM_CONNECTION (self)); - nm_connection_for_each_setting_value (for_agent, only_agent_secrets_cb, NULL); + filter_flags = NM_SETTING_SECRET_FLAG_AGENT_OWNED; + nm_connection_for_each_setting_value (for_agent, + secrets_filter_cb, + GUINT_TO_POINTER (filter_flags)); nm_agent_manager_save_secrets (priv->agent_mgr, for_agent, TRUE, sender_uid); g_object_unref (for_agent); } @@ -1272,6 +1356,8 @@ dbus_get_agent_secrets_cb (NMSettingsConnection *self, update_secrets_cache (self); hash = nm_connection_to_hash (NM_CONNECTION (self), NM_SETTING_HASH_FLAG_ONLY_SECRETS); + if (!hash) + hash = g_hash_table_new (NULL, NULL); dbus_g_method_return (context, hash); g_hash_table_destroy (hash); } @@ -1440,6 +1526,181 @@ nm_settings_connection_read_and_fill_timestamp (NMSettingsConnection *connection g_key_file_free (timestamps_file); } +static guint +mac_hash (gconstpointer v) +{ + const guint8 *p = v; + guint32 i, h = 5381; + + for (i = 0; i < ETH_ALEN; i++) + h = (h << 5) + h + p[i]; + return h; +} + +static gboolean +mac_equal (gconstpointer a, gconstpointer b) +{ + return memcmp (a, b, ETH_ALEN) == 0; +} + +static guint8 * +mac_dup (const struct ether_addr *old) +{ + guint8 *new; + + g_return_val_if_fail (old != NULL, NULL); + + new = g_malloc0 (ETH_ALEN); + memcpy (new, old, ETH_ALEN); + return new; +} + +/** + * nm_settings_connection_has_seen_bssid: + * @connection: the #NMSettingsConnection + * @bssid: the BSSID to check the seen BSSID list for + * + * Returns: TRUE if the given @bssid is in the seen BSSIDs list + **/ +gboolean +nm_settings_connection_has_seen_bssid (NMSettingsConnection *connection, + const struct ether_addr *bssid) +{ + g_return_val_if_fail (connection != NULL, FALSE); + g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (connection), FALSE); + g_return_val_if_fail (bssid != NULL, FALSE); + + return !!g_hash_table_lookup (NM_SETTINGS_CONNECTION_GET_PRIVATE (connection)->seen_bssids, bssid); +} + +/** + * nm_settings_connection_add_seen_bssid: + * @connection: the #NMSettingsConnection + * @seen_bssid: BSSID to set into the connection and to store into + * the seen-bssids database + * + * Updates the connection and seen-bssids database with the provided BSSID. + **/ +void +nm_settings_connection_add_seen_bssid (NMSettingsConnection *connection, + const struct ether_addr *seen_bssid) +{ + NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (connection); + const char *connection_uuid; + GKeyFile *seen_bssids_file; + char *data, *bssid_str; + const char **list; + gsize len; + GError *error = NULL; + GHashTableIter iter; + guint n; + + g_return_if_fail (seen_bssid != NULL); + + if (g_hash_table_lookup (priv->seen_bssids, seen_bssid)) + return; /* Already in the list */ + + /* Add the new BSSID; let the hash take ownership of the allocated BSSID string */ + bssid_str = nm_ether_ntop (seen_bssid); + g_return_if_fail (bssid_str != NULL); + g_hash_table_insert (priv->seen_bssids, mac_dup (seen_bssid), bssid_str); + + /* Build up a list of all the BSSIDs in string form */ + n = 0; + list = g_malloc0 (g_hash_table_size (priv->seen_bssids) * sizeof (char *)); + g_hash_table_iter_init (&iter, priv->seen_bssids); + while (g_hash_table_iter_next (&iter, NULL, (gpointer) &bssid_str)) + list[n++] = bssid_str; + + /* Save BSSID to seen-bssids file */ + seen_bssids_file = g_key_file_new (); + g_key_file_set_list_separator (seen_bssids_file, ','); + if (!g_key_file_load_from_file (seen_bssids_file, SETTINGS_SEEN_BSSIDS_FILE, G_KEY_FILE_KEEP_COMMENTS, &error)) { + if (!g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) { + nm_log_warn (LOGD_SETTINGS, "error parsing seen-bssids file '%s': %s", + SETTINGS_SEEN_BSSIDS_FILE, error->message); + } + g_clear_error (&error); + } + + connection_uuid = nm_connection_get_uuid (NM_CONNECTION (connection)); + g_key_file_set_string_list (seen_bssids_file, "seen-bssids", connection_uuid, list, n); + g_free (list); + + data = g_key_file_to_data (seen_bssids_file, &len, &error); + if (data) { + g_file_set_contents (SETTINGS_SEEN_BSSIDS_FILE, data, len, &error); + g_free (data); + } + g_key_file_free (seen_bssids_file); + + if (error) { + nm_log_warn (LOGD_SETTINGS, "error saving seen-bssids to file '%s': %s", + SETTINGS_SEEN_BSSIDS_FILE, error->message); + g_error_free (error); + } +} + +static void +add_seen_bssid_string (NMSettingsConnection *self, const char *bssid) +{ + struct ether_addr mac; + + g_return_if_fail (bssid != NULL); + if (ether_aton_r (bssid, &mac)) { + g_hash_table_insert (NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->seen_bssids, + mac_dup (&mac), + g_strdup (bssid)); + } +} + +/** + * nm_settings_connection_read_and_fill_seen_bssids: + * @connection: the #NMSettingsConnection + * + * Retrieves seen BSSIDs of the connection from database file and stores then into the + * connection private data. + **/ +void +nm_settings_connection_read_and_fill_seen_bssids (NMSettingsConnection *connection) +{ + NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (connection); + const char *connection_uuid; + GKeyFile *seen_bssids_file; + char **tmp_strv = NULL; + gsize i, len = 0; + NMSettingWireless *s_wifi; + + /* Get seen BSSIDs from database file */ + seen_bssids_file = g_key_file_new (); + g_key_file_set_list_separator (seen_bssids_file, ','); + if (g_key_file_load_from_file (seen_bssids_file, SETTINGS_SEEN_BSSIDS_FILE, G_KEY_FILE_KEEP_COMMENTS, NULL)) { + connection_uuid = nm_connection_get_uuid (NM_CONNECTION (connection)); + tmp_strv = g_key_file_get_string_list (seen_bssids_file, "seen-bssids", connection_uuid, &len, NULL); + } + g_key_file_free (seen_bssids_file); + + /* Update connection's seen-bssids */ + if (tmp_strv) { + g_hash_table_remove_all (priv->seen_bssids); + for (i = 0; i < len; i++) + add_seen_bssid_string (connection, tmp_strv[i]); + g_strfreev (tmp_strv); + } else { + /* If this connection didn't have an entry in the seen-bssids database, + * maybe this is the first time we've read it in, so populate the + * seen-bssids list from the deprecated seen-bssids property of the + * wifi setting. + */ + s_wifi = nm_connection_get_setting_wireless (NM_CONNECTION (connection)); + if (s_wifi) { + len = nm_setting_wireless_get_num_seen_bssids (s_wifi); + for (i = 0; i < len; i++) + add_seen_bssid_string (connection, nm_setting_wireless_get_seen_bssid (s_wifi, i)); + } + } +} + /**************************************************************/ static void @@ -1463,6 +1724,8 @@ nm_settings_connection_init (NMSettingsConnection *self) self); priv->agent_mgr = nm_agent_manager_get (); + + priv->seen_bssids = g_hash_table_new_full (mac_hash, mac_equal, g_free, g_free); } static void @@ -1490,6 +1753,8 @@ dispose (GObject *object) nm_agent_manager_cancel_secrets (priv->agent_mgr, GPOINTER_TO_UINT (iter->data)); g_slist_free (priv->reqs); + g_hash_table_destroy (priv->seen_bssids); + set_visible (self, FALSE); if (priv->session_changed_id) diff --git a/src/settings/nm-settings-connection.h b/src/settings/nm-settings-connection.h index 116bfdcc..bc9e3c47 100644 --- a/src/settings/nm-settings-connection.h +++ b/src/settings/nm-settings-connection.h @@ -24,6 +24,7 @@ #include <nm-connection.h> #include "nm-settings-flags.h" +#include <net/ethernet.h> G_BEGIN_DECLS @@ -116,6 +117,9 @@ gboolean nm_settings_connection_is_visible (NMSettingsConnection *self); void nm_settings_connection_recheck_visibility (NMSettingsConnection *self); +gboolean nm_settings_connection_check_permission (NMSettingsConnection *self, + const char *permission); + void nm_settings_connection_signal_remove (NMSettingsConnection *self); guint64 nm_settings_connection_get_timestamp (NMSettingsConnection *connection); @@ -124,6 +128,14 @@ void nm_settings_connection_update_timestamp (NMSettingsConnection *connection, void nm_settings_connection_read_and_fill_timestamp (NMSettingsConnection *connection); +gboolean nm_settings_connection_has_seen_bssid (NMSettingsConnection *connection, + const struct ether_addr *bssid); + +void nm_settings_connection_add_seen_bssid (NMSettingsConnection *connection, + const struct ether_addr *seen_bssid); + +void nm_settings_connection_read_and_fill_seen_bssids (NMSettingsConnection *connection); + G_END_DECLS #endif /* NM_SETTINGS_CONNECTION_H */ diff --git a/src/settings/nm-settings.c b/src/settings/nm-settings.c index e23e8d13..733e9145 100644 --- a/src/settings/nm-settings.c +++ b/src/settings/nm-settings.c @@ -135,6 +135,7 @@ enum { CONNECTION_REMOVED, CONNECTION_VISIBILITY_CHANGED, CONNECTIONS_LOADED, + AGENT_REGISTERED, NEW_CONNECTION, /* exported, not used internally */ LAST_SIGNAL @@ -699,6 +700,18 @@ connection_visibility_changed (NMSettingsConnection *connection, connection); } +static void +secret_agent_registered (NMAgentManager *agent_mgr, + NMSecretAgent *agent, + gpointer user_data) +{ + /* Re-emit for listeners like NMPolicy */ + g_signal_emit (NM_SETTINGS (user_data), + signals[AGENT_REGISTERED], + 0, + agent); +} + #define NM_DBUS_SERVICE_OPENCONNECT "org.freedesktop.NetworkManager.openconnect" #define NM_OPENCONNECT_KEY_GATEWAY "gateway" #define NM_OPENCONNECT_KEY_COOKIE "cookie" @@ -773,6 +786,9 @@ claim_connection (NMSettings *self, /* Read timestamp from look-aside file and put it into the connection's data */ nm_settings_connection_read_and_fill_timestamp (connection); + /* Read seen-bssids from look-aside file and put it into the connection's data */ + nm_settings_connection_read_and_fill_seen_bssids (connection); + /* Ensure it's initial visibility is up-to-date */ nm_settings_connection_recheck_visibility (connection); @@ -1527,6 +1543,8 @@ nm_settings_init (NMSettings *self) * recreated often. */ priv->agent_mgr = nm_agent_manager_get (); + + g_signal_connect (priv->agent_mgr, "agent-registered", G_CALLBACK (secret_agent_registered), self); } static void @@ -1690,6 +1708,16 @@ nm_settings_class_init (NMSettingsClass *class) g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); + signals[AGENT_REGISTERED] = + g_signal_new (NM_SETTINGS_SIGNAL_AGENT_REGISTERED, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMSettingsClass, agent_registered), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, G_TYPE_OBJECT); + + signals[NEW_CONNECTION] = g_signal_new ("new-connection", G_OBJECT_CLASS_TYPE (object_class), diff --git a/src/settings/nm-settings.h b/src/settings/nm-settings.h index a5cb4d7c..66d41cce 100644 --- a/src/settings/nm-settings.h +++ b/src/settings/nm-settings.h @@ -19,7 +19,7 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * (C) Copyright 2007 - 2010 Red Hat, Inc. + * (C) Copyright 2007 - 2011 Red Hat, Inc. * (C) Copyright 2008 Novell, Inc. */ @@ -31,6 +31,7 @@ #include "nm-settings-connection.h" #include "nm-system-config-interface.h" #include "nm-device.h" +#include "nm-secret-agent.h" #define NM_TYPE_SETTINGS (nm_settings_get_type ()) #define NM_SETTINGS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTINGS, NMSettings)) @@ -48,6 +49,7 @@ #define NM_SETTINGS_SIGNAL_CONNECTION_REMOVED "connection-removed" #define NM_SETTINGS_SIGNAL_CONNECTION_VISIBILITY_CHANGED "connection-visibility-changed" #define NM_SETTINGS_SIGNAL_CONNECTIONS_LOADED "connections-loaded" +#define NM_SETTINGS_SIGNAL_AGENT_REGISTERED "agent-registered" typedef struct { GObject parent_instance; @@ -68,6 +70,8 @@ typedef struct { void (*connection_visibility_changed) (NMSettings *self, NMSettingsConnection *connection); void (*connections_loaded) (NMSettings *self); + + void (*agent_registered) (NMSettings *self, NMSecretAgent *agent); } NMSettingsClass; GType nm_settings_get_type (void); diff --git a/src/settings/plugins/ifcfg-rh/nm-ifcfg-connection.c b/src/settings/plugins/ifcfg-rh/nm-ifcfg-connection.c index 433f933b..04d3d3e4 100644 --- a/src/settings/plugins/ifcfg-rh/nm-ifcfg-connection.c +++ b/src/settings/plugins/ifcfg-rh/nm-ifcfg-connection.c @@ -15,7 +15,7 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * Copyright (C) 2008 - 2010 Red Hat, Inc. + * Copyright (C) 2008 - 2011 Red Hat, Inc. */ #include <string.h> @@ -181,12 +181,13 @@ nm_ifcfg_connection_get_unmanaged_spec (NMIfcfgConnection *self) static void commit_changes (NMSettingsConnection *connection, NMSettingsConnectionCommitFunc callback, - gpointer user_data) + gpointer user_data) { NMIfcfgConnectionPrivate *priv = NM_IFCFG_CONNECTION_GET_PRIVATE (connection); GError *error = NULL; NMConnection *reread; char *unmanaged = NULL, *keyfile = NULL, *routefile = NULL, *route6file = NULL; + gboolean same = FALSE; /* To ensure we don't rewrite files that are only changed from other * processes on-disk, read the existing connection back in and only rewrite @@ -200,28 +201,36 @@ commit_changes (NMSettingsConnection *connection, g_free (routefile); g_free (route6file); - if (reread && nm_connection_compare (NM_CONNECTION (connection), - reread, - NM_SETTING_COMPARE_FLAG_EXACT)) - goto out; + if (reread) { + same = nm_connection_compare (NM_CONNECTION (connection), + reread, + NM_SETTING_COMPARE_FLAG_IGNORE_AGENT_OWNED_SECRETS | + NM_SETTING_COMPARE_FLAG_IGNORE_NOT_SAVED_SECRETS); + g_object_unref (reread); - if (!writer_update_connection (NM_CONNECTION (connection), - IFCFG_DIR, - priv->path, - priv->keyfile, - &error)) { + /* Don't bother writing anything out if in-memory and on-disk data are the same */ + if (same) { + /* But chain up to parent to handle success - emits updated signal */ + NM_SETTINGS_CONNECTION_CLASS (nm_ifcfg_connection_parent_class)->commit_changes (connection, callback, user_data); + return; + } + } + + if (writer_update_connection (NM_CONNECTION (connection), + IFCFG_DIR, + priv->path, + priv->keyfile, + &error)) { + /* Chain up to parent to handle success */ + NM_SETTINGS_CONNECTION_CLASS (nm_ifcfg_connection_parent_class)->commit_changes (connection, callback, user_data); + } else { + /* Otherwise immediate error */ callback (connection, error, user_data); g_error_free (error); - return; } - -out: - if (reread) - g_object_unref (reread); - NM_SETTINGS_CONNECTION_CLASS (nm_ifcfg_connection_parent_class)->commit_changes (connection, callback, user_data); } -static void +static void do_delete (NMSettingsConnection *connection, NMSettingsConnectionDeleteFunc callback, gpointer user_data) diff --git a/src/settings/plugins/ifcfg-rh/plugin.c b/src/settings/plugins/ifcfg-rh/plugin.c index 7915c467..ed0dceca 100644 --- a/src/settings/plugins/ifcfg-rh/plugin.c +++ b/src/settings/plugins/ifcfg-rh/plugin.c @@ -269,19 +269,24 @@ connection_new_or_changed (SCPluginIfcfg *self, /* Successfully read connection changes */ - /* When the connections are the same, nothing is done */ - if (nm_connection_compare (NM_CONNECTION (existing), - NM_CONNECTION (new), - NM_SETTING_COMPARE_FLAG_EXACT)) { + old_unmanaged = nm_ifcfg_connection_get_unmanaged_spec (NM_IFCFG_CONNECTION (existing)); + new_unmanaged = nm_ifcfg_connection_get_unmanaged_spec (NM_IFCFG_CONNECTION (new)); + + /* When interface is unmanaged or the connections and unmanaged specs are the same + * there's nothing to do */ + if ( (g_strcmp0 (old_unmanaged, new_unmanaged) == 0 && new_unmanaged != NULL) + || ( nm_connection_compare (NM_CONNECTION (existing), + NM_CONNECTION (new), + NM_SETTING_COMPARE_FLAG_IGNORE_AGENT_OWNED_SECRETS | + NM_SETTING_COMPARE_FLAG_IGNORE_NOT_SAVED_SECRETS) + && g_strcmp0 (old_unmanaged, new_unmanaged) == 0)) { + g_object_unref (new); return; } PLUGIN_PRINT (IFCFG_PLUGIN_NAME, "updating %s", path); - old_unmanaged = nm_ifcfg_connection_get_unmanaged_spec (NM_IFCFG_CONNECTION (existing)); - new_unmanaged = nm_ifcfg_connection_get_unmanaged_spec (NM_IFCFG_CONNECTION (new)); - if (new_unmanaged) { if (!old_unmanaged) { /* Unexport the connection by telling the settings service it's @@ -289,6 +294,11 @@ connection_new_or_changed (SCPluginIfcfg *self, * unmanaged specs have changed. */ nm_settings_connection_signal_remove (NM_SETTINGS_CONNECTION (existing)); + /* Remove the path so that claim_connection() doesn't complain later when + * interface gets managed and connection is re-added. */ + nm_connection_set_path (NM_CONNECTION (existing), NULL); + + g_object_set (existing, NM_IFCFG_CONNECTION_UNMANAGED, new_unmanaged, NULL); g_signal_emit_by_name (self, NM_SYSTEM_CONFIG_INTERFACE_UNMANAGED_SPECS_CHANGED); } } else { diff --git a/src/settings/plugins/ifcfg-rh/reader.c b/src/settings/plugins/ifcfg-rh/reader.c index a6f9ca85..cdf5889e 100644 --- a/src/settings/plugins/ifcfg-rh/reader.c +++ b/src/settings/plugins/ifcfg-rh/reader.c @@ -176,9 +176,9 @@ read_mac_address (shvarFile *ifcfg, const char *key, GByteArray **array, GError mac = ether_aton (value); if (!mac) { - g_free (value); g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "%s: the MAC address '%s' was invalid.", key, value); + g_free (value); return FALSE; } @@ -574,7 +574,8 @@ read_full_ip4_address (shvarFile *ifcfg, if (!nm_ip4_address_get_prefix (addr)) { if (!read_ip4_address (ifcfg, netmask_tag, &tmp, error)) goto error; - nm_ip4_address_set_prefix (addr, nm_utils_ip4_netmask_to_prefix (tmp)); + if (tmp) + nm_ip4_address_set_prefix (addr, nm_utils_ip4_netmask_to_prefix (tmp)); } /* Try to autodetermine the prefix for the address' class */ @@ -661,7 +662,8 @@ read_one_ip4_route (shvarFile *ifcfg, /* Prefix */ if (!read_ip4_address (ifcfg, netmask_tag, &tmp, error)) goto out; - nm_ip4_route_set_prefix (route, nm_utils_ip4_netmask_to_prefix (tmp)); + if (tmp) + nm_ip4_route_set_prefix (route, nm_utils_ip4_netmask_to_prefix (tmp)); /* Validate the prefix */ if ( !nm_ip4_route_get_prefix (route) @@ -788,7 +790,7 @@ read_route_file_legacy (const char *filename, NMSettingIP4Config *s_ip4, GError if (prefix) { errno = 0; prefix_int = strtol (prefix, NULL, 10); - if (errno || prefix_int < 0 || prefix_int > 32) { + if (errno || prefix_int <= 0 || prefix_int > 32) { g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Invalid IP4 route destination prefix '%s'", prefix); g_free (prefix); @@ -973,9 +975,9 @@ read_route6_file (const char *filename, NMSettingIP6Config *s_ip6, GError **erro const char *pattern_empty = "^\\s*(\\#.*)?$"; const char *pattern_to1 = "^\\s*(" IPV6_ADDR_REGEX "|default)" /* IPv6 or 'default' keyword */ - "(?:/(\\d{1,2}))?"; /* optional prefix */ + "(?:/(\\d{1,3}))?"; /* optional prefix */ const char *pattern_to2 = "to\\s+(" IPV6_ADDR_REGEX "|default)" /* IPv6 or 'default' keyword */ - "(?:/(\\d{1,2}))?"; /* optional prefix */ + "(?:/(\\d{1,3}))?"; /* optional prefix */ const char *pattern_via = "via\\s+(" IPV6_ADDR_REGEX ")"; /* IPv6 of gateway */ const char *pattern_metric = "metric\\s+(\\d+)"; /* metric */ @@ -1041,7 +1043,7 @@ read_route6_file (const char *filename, NMSettingIP6Config *s_ip6, GError **erro if (prefix) { errno = 0; prefix_int = strtol (prefix, NULL, 10); - if (errno || prefix_int < 0 || prefix_int > 128) { + if (errno || prefix_int <= 0 || prefix_int > 128) { g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Invalid IP6 route destination prefix '%s'", prefix); g_free (prefix); @@ -1228,15 +1230,11 @@ make_ip4_setting (shvarFile *ifcfg, && !tmp_ip4_0 && !tmp_prefix_0 && !tmp_netmask_0 && !tmp_ip4_1 && !tmp_prefix_1 && !tmp_netmask_1 && !tmp_ip4_2 && !tmp_prefix_2 && !tmp_netmask_2) { - if (valid_ip6_config) { + if (valid_ip6_config) /* Nope, no IPv4 */ - g_object_set (s_ip4, - NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_DISABLED, - NULL); - return NM_SETTING (s_ip4); - } - - method = NM_SETTING_IP4_CONFIG_METHOD_AUTO; + method = NM_SETTING_IP4_CONFIG_METHOD_DISABLED; + else + method = NM_SETTING_IP4_CONFIG_METHOD_AUTO; } g_free (tmp_ip4); g_free (tmp_prefix); @@ -1260,6 +1258,9 @@ make_ip4_setting (shvarFile *ifcfg, NM_SETTING_IP4_CONFIG_MAY_FAIL, !svTrueValue (ifcfg, "IPV4_FAILURE_FATAL", TRUE), NULL); + if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED) == 0) + return NM_SETTING (s_ip4); + /* Handle manual settings */ if (!strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_MANUAL)) { NMIP4Address *addr; @@ -1418,7 +1419,7 @@ make_ip6_setting (shvarFile *ifcfg, char *value = NULL; char *str_value; char *route6_path = NULL; - gboolean bool_value, ipv6forwarding, ipv6_autoconf, dhcp6 = FALSE; + gboolean ipv6init, ipv6forwarding, ipv6_autoconf, dhcp6 = FALSE; char *method = NM_SETTING_IP6_CONFIG_METHOD_MANUAL; guint32 i; shvarFile *network_ifcfg; @@ -1431,26 +1432,6 @@ make_ip6_setting (shvarFile *ifcfg, return NULL; } - /* Is IPV6 enabled? Set method to "ignored", when not enabled */ - str_value = svGetValue (ifcfg, "IPV6INIT", FALSE); - bool_value = svTrueValue (ifcfg, "IPV6INIT", FALSE); - if (!str_value) { - network_ifcfg = svNewFile (network_file); - if (network_ifcfg) { - bool_value = svTrueValue (network_ifcfg, "IPV6INIT", FALSE); - svCloseFile (network_ifcfg); - } - } - g_free (str_value); - - if (!bool_value) { - /* IPv6 is disabled */ - g_object_set (s_ip6, - NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, - NULL); - return NM_SETTING (s_ip6); - } - /* First check if IPV6_DEFROUTE is set for this device; IPV6_DEFROUTE has the * opposite meaning from never-default. The default if IPV6_DEFROUTE is not * specified is IPV6_DEFROUTE=yes which means that this connection can be used @@ -1493,23 +1474,39 @@ make_ip6_setting (shvarFile *ifcfg, } /* Find out method property */ - ipv6forwarding = svTrueValue (ifcfg, "IPV6FORWARDING", FALSE); - ipv6_autoconf = svTrueValue (ifcfg, "IPV6_AUTOCONF", !ipv6forwarding); - dhcp6 = svTrueValue (ifcfg, "DHCPV6C", FALSE); - - if (ipv6_autoconf) - method = NM_SETTING_IP6_CONFIG_METHOD_AUTO; - else if (dhcp6) - method = NM_SETTING_IP6_CONFIG_METHOD_DHCP; + /* Is IPV6 enabled? Set method to "ignored", when not enabled */ + str_value = svGetValue (ifcfg, "IPV6INIT", FALSE); + ipv6init = svTrueValue (ifcfg, "IPV6INIT", FALSE); + if (!str_value) { + network_ifcfg = svNewFile (network_file); + if (network_ifcfg) { + ipv6init = svTrueValue (network_ifcfg, "IPV6INIT", FALSE); + svCloseFile (network_ifcfg); + } + } + g_free (str_value); + + if (!ipv6init) + method = NM_SETTING_IP6_CONFIG_METHOD_IGNORE; /* IPv6 is disabled */ else { - /* IPV6_AUTOCONF=no and no IPv6 address -> method 'link-local' */ - str_value = svGetValue (ifcfg, "IPV6ADDR", FALSE); - if (!str_value) - str_value = svGetValue (ifcfg, "IPV6ADDR_SECONDARIES", FALSE); + ipv6forwarding = svTrueValue (ifcfg, "IPV6FORWARDING", FALSE); + ipv6_autoconf = svTrueValue (ifcfg, "IPV6_AUTOCONF", !ipv6forwarding); + dhcp6 = svTrueValue (ifcfg, "DHCPV6C", FALSE); + + if (ipv6_autoconf) + method = NM_SETTING_IP6_CONFIG_METHOD_AUTO; + else if (dhcp6) + method = NM_SETTING_IP6_CONFIG_METHOD_DHCP; + else { + /* IPV6_AUTOCONF=no and no IPv6 address -> method 'link-local' */ + str_value = svGetValue (ifcfg, "IPV6ADDR", FALSE); + if (!str_value) + str_value = svGetValue (ifcfg, "IPV6ADDR_SECONDARIES", FALSE); - if (!str_value) - method = NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL; - g_free (str_value); + if (!str_value) + method = NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL; + g_free (str_value); + } } /* TODO - handle other methods */ @@ -1521,6 +1518,10 @@ make_ip6_setting (shvarFile *ifcfg, NM_SETTING_IP6_CONFIG_MAY_FAIL, !svTrueValue (ifcfg, "IPV6_FAILURE_FATAL", FALSE), NULL); + /* Don't bother to read IP, DNS and routes when IPv6 is disabled */ + if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE) == 0) + return NM_SETTING (s_ip6); + if (!strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_MANUAL)) { NMIP6Address *addr; char *val; @@ -2234,6 +2235,7 @@ eap_peap_reader (const char *eap_method, gboolean phase2, GError **error) { + char *anon_ident = NULL; char *ca_cert = NULL; char *real_cert_path = NULL; char *inner_auth = NULL; @@ -2275,6 +2277,10 @@ eap_peap_reader (const char *eap_method, if (svTrueValue (ifcfg, "IEEE_8021X_PEAP_FORCE_NEW_LABEL", FALSE)) g_object_set (s_8021x, NM_SETTING_802_1X_PHASE1_PEAPLABEL, "1", NULL); + anon_ident = svGetValue (ifcfg, "IEEE_8021X_ANON_IDENTITY", FALSE); + if (anon_ident && strlen (anon_ident)) + g_object_set (s_8021x, NM_SETTING_802_1X_ANONYMOUS_IDENTITY, anon_ident, NULL); + inner_auth = svGetValue (ifcfg, "IEEE_8021X_INNER_AUTH_METHODS", FALSE); if (!inner_auth) { g_set_error (error, IFCFG_PLUGIN_ERROR, 0, @@ -2324,6 +2330,7 @@ done: g_free (peapver); g_free (real_cert_path); g_free (ca_cert); + g_free (anon_ident); return success; } @@ -2737,6 +2744,7 @@ make_wireless_setting (shvarFile *ifcfg, { NMSettingWireless *s_wireless; GByteArray *array = NULL; + GSList *macaddr_blacklist = NULL; char *value; s_wireless = NM_SETTING_WIRELESS (nm_setting_wireless_new ()); @@ -2770,6 +2778,33 @@ make_wireless_setting (shvarFile *ifcfg, g_object_set (s_wireless, NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS, array, NULL); g_byte_array_free (array, TRUE); } + } else { + PLUGIN_WARN (IFCFG_PLUGIN_NAME, " warning: %s", (*error)->message); + g_clear_error (error); + } + + value = svGetValue (ifcfg, "HWADDR_BLACKLIST", FALSE); + if (value) { + char **list = NULL, **iter; + struct ether_addr addr; + + list = g_strsplit_set (value, " \t", 0); + for (iter = list; iter && *iter; iter++) { + if (**iter == '\0') + continue; + if (!ether_aton_r (*iter, &addr)) { + PLUGIN_WARN (IFCFG_PLUGIN_NAME, " warning: invalid MAC in HWADDR_BLACKLIST '%s'", *iter); + continue; + } + macaddr_blacklist = g_slist_prepend (macaddr_blacklist, *iter); + } + if (macaddr_blacklist) { + macaddr_blacklist = g_slist_reverse (macaddr_blacklist); + g_object_set (s_wireless, NM_SETTING_WIRELESS_MAC_ADDRESS_BLACKLIST, macaddr_blacklist, NULL); + g_slist_free (macaddr_blacklist); + } + g_free (value); + g_strfreev (list); } value = svGetValue (ifcfg, "ESSID", TRUE); @@ -3030,6 +3065,7 @@ make_wired_setting (shvarFile *ifcfg, char *value = NULL; int mtu; GByteArray *mac = NULL; + GSList *macaddr_blacklist = NULL; char *nettype; s_wired = NM_SETTING_WIRED (nm_setting_wired_new ()); @@ -3162,6 +3198,33 @@ make_wired_setting (shvarFile *ifcfg, g_object_set (s_wired, NM_SETTING_WIRED_CLONED_MAC_ADDRESS, mac, NULL); g_byte_array_free (mac, TRUE); } + } else { + PLUGIN_WARN (IFCFG_PLUGIN_NAME, " warning: %s", (*error)->message); + g_clear_error (error); + } + + value = svGetValue (ifcfg, "HWADDR_BLACKLIST", FALSE); + if (value) { + char **list = NULL, **iter; + struct ether_addr addr; + + list = g_strsplit_set (value, " \t", 0); + for (iter = list; iter && *iter; iter++) { + if (**iter == '\0') + continue; + if (!ether_aton_r (*iter, &addr)) { + PLUGIN_WARN (IFCFG_PLUGIN_NAME, " warning: invalid MAC in HWADDR_BLACKLIST '%s'", *iter); + continue; + } + macaddr_blacklist = g_slist_prepend (macaddr_blacklist, *iter); + } + if (macaddr_blacklist) { + macaddr_blacklist = g_slist_reverse (macaddr_blacklist); + g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS_BLACKLIST, macaddr_blacklist, NULL); + g_slist_free (macaddr_blacklist); + } + g_free (value); + g_strfreev (list); } value = svGetValue (ifcfg, "KEY_MGMT", FALSE); @@ -3246,7 +3309,7 @@ is_wireless_device (const char *iface) g_return_val_if_fail (iface != NULL, FALSE); fd = socket(AF_INET, SOCK_DGRAM, 0); - if (!fd) + if (fd == -1) return FALSE; memset (&wrq, 0, sizeof (struct iwreq)); diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-8021x-peap-mschapv2 b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-8021x-peap-mschapv2 index 6d68eca1..27bcbbf9 100644 --- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-8021x-peap-mschapv2 +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-8021x-peap-mschapv2 @@ -12,4 +12,4 @@ IEEE_8021X_CA_CERT=test_ca_cert.pem IEEE_8021X_PEAP_VERSION=1 IEEE_8021X_PEAP_FORCE_NEW_LABEL=yes IEEE_8021X_INNER_AUTH_METHODS=MSCHAPV2 - +IEEE_8021X_ANON_IDENTITY=somebody diff --git a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c index e6013f8d..d1f08aa9 100644 --- a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c +++ b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c @@ -2707,6 +2707,7 @@ test_read_wired_8021x_peap_mschapv2 (void) GError *error = NULL; const char *tmp; const char *expected_identity = "David Smith"; + const char *expected_anon_identity = "somebody"; const char *expected_password = "foobar baz"; gboolean success = FALSE; const char *expected_ca_cert_path; @@ -2793,6 +2794,19 @@ test_read_wired_8021x_peap_mschapv2 (void) NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_IDENTITY); + /* Anonymous Identity */ + tmp = nm_setting_802_1x_get_anonymous_identity (s_8021x); + ASSERT (tmp != NULL, + "wired-8021x-peap-mschapv2-verify-8021x", "failed to verify %s: missing %s / %s key", + TEST_IFCFG_WIRED_8021x_PEAP_MSCHAPV2, + NM_SETTING_802_1X_SETTING_NAME, + NM_SETTING_802_1X_ANONYMOUS_IDENTITY); + ASSERT (strcmp (tmp, expected_anon_identity) == 0, + "wired-8021x-peap-mschapv2-verify-8021x", "failed to verify %s: unexpected %s / %s key value", + TEST_IFCFG_WIRED_8021x_PEAP_MSCHAPV2, + NM_SETTING_802_1X_SETTING_NAME, + NM_SETTING_802_1X_ANONYMOUS_IDENTITY); + /* Password */ tmp = nm_setting_802_1x_get_password (s_8021x); ASSERT (tmp != NULL, @@ -6491,7 +6505,7 @@ test_write_wired_static (void) struct in6_addr ip6, ip6_1, ip6_2; struct in6_addr route1_dest, route2_dest, route1_nexthop, route2_nexthop; struct in6_addr dns6_1, dns6_2; - const guint32 route1_prefix = 64, route2_prefix = 0; + const guint32 route1_prefix = 64, route2_prefix = 128; const guint32 route1_metric = 99, route2_metric = 1; NMIP4Address *addr; NMIP6Address *addr6; @@ -6774,6 +6788,7 @@ test_write_wired_dhcp (void) g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + NM_SETTING_IP6_CONFIG_MAY_FAIL, TRUE, NULL); /* Save the ifcfg */ @@ -7234,7 +7249,10 @@ test_write_wired_static_routes (void) NM_SETTING_IP6_CONFIG_SETTING_NAME); nm_connection_add_setting (connection, NM_SETTING (s_ip6)); - g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, NULL); + g_object_set (s_ip6, + NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + NM_SETTING_IP6_CONFIG_MAY_FAIL, TRUE, + NULL); ASSERT (nm_connection_verify (connection, &error) == TRUE, "wired-static-routes-write", "failed to verify connection: %s", @@ -7350,7 +7368,10 @@ test_write_wired_dhcp_8021x_peap_mschapv2 (void) NM_SETTING_IP6_CONFIG_SETTING_NAME); nm_connection_add_setting (connection, NM_SETTING (s_ip6)); - g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, NULL); + g_object_set (s_ip6, + NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + NM_SETTING_IP6_CONFIG_MAY_FAIL, TRUE, + NULL); /* 802.1x setting */ s_8021x = (NMSetting8021x *) nm_setting_802_1x_new (); @@ -7361,6 +7382,7 @@ test_write_wired_dhcp_8021x_peap_mschapv2 (void) g_object_set (s_8021x, NM_SETTING_802_1X_IDENTITY, "Bob Saget", + NM_SETTING_802_1X_ANONYMOUS_IDENTITY, "barney", NM_SETTING_802_1X_PASSWORD, "Kids, it was back in October 2008...", NM_SETTING_802_1X_PHASE1_PEAPVER, "1", NM_SETTING_802_1X_PHASE1_PEAPLABEL, "1", @@ -7508,7 +7530,10 @@ test_write_wired_8021x_tls (NMSetting8021xCKScheme scheme, /* IP6 setting */ s_ip6 = (NMSettingIP6Config *) nm_setting_ip6_config_new (); g_assert (s_ip6); - g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, NULL); + g_object_set (s_ip6, + NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + NM_SETTING_IP6_CONFIG_MAY_FAIL, TRUE, + NULL); nm_connection_add_setting (connection, NM_SETTING (s_ip6)); /* 802.1x setting */ @@ -7753,7 +7778,10 @@ test_write_wifi_open (void) NM_SETTING_IP6_CONFIG_SETTING_NAME); nm_connection_add_setting (connection, NM_SETTING (s_ip6)); - g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, NULL); + g_object_set (s_ip6, + NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + NM_SETTING_IP6_CONFIG_MAY_FAIL, TRUE, + NULL); ASSERT (nm_connection_verify (connection, &error) == TRUE, "wifi-open-write", "failed to verify connection: %s", @@ -7892,7 +7920,10 @@ test_write_wifi_open_hex_ssid (void) NM_SETTING_IP6_CONFIG_SETTING_NAME); nm_connection_add_setting (connection, NM_SETTING (s_ip6)); - g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, NULL); + g_object_set (s_ip6, + NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + NM_SETTING_IP6_CONFIG_MAY_FAIL, TRUE, + NULL); ASSERT (nm_connection_verify (connection, &error) == TRUE, "wifi-open-hex-ssid-write", "failed to verify connection: %s", @@ -8035,7 +8066,10 @@ test_write_wifi_wep (void) NM_SETTING_IP6_CONFIG_SETTING_NAME); nm_connection_add_setting (connection, NM_SETTING (s_ip6)); - g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, NULL); + g_object_set (s_ip6, + NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + NM_SETTING_IP6_CONFIG_MAY_FAIL, TRUE, + NULL); ASSERT (nm_connection_verify (connection, &error) == TRUE, "wifi-wep-write", "failed to verify connection: %s", @@ -8198,7 +8232,10 @@ test_write_wifi_wep_adhoc (void) NM_SETTING_IP6_CONFIG_SETTING_NAME); nm_connection_add_setting (connection, NM_SETTING (s_ip6)); - g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, NULL); + g_object_set (s_ip6, + NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + NM_SETTING_IP6_CONFIG_MAY_FAIL, TRUE, + NULL); ASSERT (nm_connection_verify (connection, &error) == TRUE, "wifi-wep-adhoc-write", "failed to verify connection: %s", @@ -8351,7 +8388,10 @@ test_write_wifi_wep_passphrase (void) NM_SETTING_IP6_CONFIG_SETTING_NAME); nm_connection_add_setting (connection, NM_SETTING (s_ip6)); - g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, NULL); + g_object_set (s_ip6, + NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + NM_SETTING_IP6_CONFIG_MAY_FAIL, TRUE, + NULL); ASSERT (nm_connection_verify (connection, &error) == TRUE, "wifi-wep-passphrase-write", "failed to verify connection: %s", @@ -8506,7 +8546,10 @@ test_write_wifi_wep_40_ascii (void) NM_SETTING_IP6_CONFIG_SETTING_NAME); nm_connection_add_setting (connection, NM_SETTING (s_ip6)); - g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, NULL); + g_object_set (s_ip6, + NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + NM_SETTING_IP6_CONFIG_MAY_FAIL, TRUE, + NULL); ASSERT (nm_connection_verify (connection, &error) == TRUE, "wifi-wep-40-ascii-write", "failed to verify connection: %s", @@ -8661,7 +8704,10 @@ test_write_wifi_wep_104_ascii (void) NM_SETTING_IP6_CONFIG_SETTING_NAME); nm_connection_add_setting (connection, NM_SETTING (s_ip6)); - g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, NULL); + g_object_set (s_ip6, + NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + NM_SETTING_IP6_CONFIG_MAY_FAIL, TRUE, + NULL); ASSERT (nm_connection_verify (connection, &error) == TRUE, "wifi-wep-104-ascii-write", "failed to verify connection: %s", @@ -8813,7 +8859,10 @@ test_write_wifi_leap (void) NM_SETTING_IP6_CONFIG_SETTING_NAME); nm_connection_add_setting (connection, NM_SETTING (s_ip6)); - g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, NULL); + g_object_set (s_ip6, + NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + NM_SETTING_IP6_CONFIG_MAY_FAIL, TRUE, + NULL); ASSERT (nm_connection_verify (connection, &error) == TRUE, "wifi-leap-write", "failed to verify connection: %s", @@ -8949,7 +8998,11 @@ test_write_wifi_leap_secret_flags (NMSettingSecretFlags flags) s_ip6 = (NMSettingIP6Config *) nm_setting_ip6_config_new (); g_assert (s_ip6); nm_connection_add_setting (connection, NM_SETTING (s_ip6)); - g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, NULL); + + g_object_set (s_ip6, + NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + NM_SETTING_IP6_CONFIG_MAY_FAIL, TRUE, + NULL); success = nm_connection_verify (connection, &error); g_assert_no_error (error); @@ -9114,7 +9167,10 @@ test_write_wifi_wpa_psk (const char *name, NM_SETTING_IP6_CONFIG_SETTING_NAME); nm_connection_add_setting (connection, NM_SETTING (s_ip6)); - g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, NULL); + g_object_set (s_ip6, + NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + NM_SETTING_IP6_CONFIG_MAY_FAIL, TRUE, + NULL); ASSERT (nm_connection_verify (connection, &error) == TRUE, test_name, "failed to verify connection: %s", @@ -9277,7 +9333,10 @@ test_write_wifi_wpa_psk_adhoc (void) NM_SETTING_IP6_CONFIG_SETTING_NAME); nm_connection_add_setting (connection, NM_SETTING (s_ip6)); - g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, NULL); + g_object_set (s_ip6, + NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + NM_SETTING_IP6_CONFIG_MAY_FAIL, TRUE, + NULL); ASSERT (nm_connection_verify (connection, &error) == TRUE, "wifi-wpa-psk-adhoc-write", "failed to verify connection: %s", @@ -9458,7 +9517,10 @@ test_write_wifi_wpa_eap_tls (void) NM_SETTING_IP6_CONFIG_SETTING_NAME); nm_connection_add_setting (connection, NM_SETTING (s_ip6)); - g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, NULL); + g_object_set (s_ip6, + NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + NM_SETTING_IP6_CONFIG_MAY_FAIL, TRUE, + NULL); ASSERT (nm_connection_verify (connection, &error) == TRUE, "wifi-wpa-eap-tls-write", "failed to verify connection: %s", @@ -9657,7 +9719,10 @@ test_write_wifi_wpa_eap_ttls_tls (void) NM_SETTING_IP6_CONFIG_SETTING_NAME); nm_connection_add_setting (connection, NM_SETTING (s_ip6)); - g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, NULL); + g_object_set (s_ip6, + NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + NM_SETTING_IP6_CONFIG_MAY_FAIL, TRUE, + NULL); ASSERT (nm_connection_verify (connection, &error) == TRUE, "wifi-wpa-eap-ttls-tls-write", "failed to verify connection: %s", @@ -9828,7 +9893,10 @@ test_write_wifi_wpa_eap_ttls_mschapv2 (void) NM_SETTING_IP6_CONFIG_SETTING_NAME); nm_connection_add_setting (connection, NM_SETTING (s_ip6)); - g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, NULL); + g_object_set (s_ip6, + NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + NM_SETTING_IP6_CONFIG_MAY_FAIL, TRUE, + NULL); ASSERT (nm_connection_verify (connection, &error) == TRUE, "wifi-wpa-eap-ttls-mschapv2-write", "failed to verify connection: %s", @@ -9970,7 +10038,10 @@ test_write_wifi_wpa_then_open (void) g_assert (s_ip6); nm_connection_add_setting (connection, NM_SETTING (s_ip6)); - g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, NULL); + g_object_set (s_ip6, + NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + NM_SETTING_IP6_CONFIG_MAY_FAIL, TRUE, + NULL); success = nm_connection_verify (connection, &error); g_assert_no_error (error); @@ -10154,9 +10225,13 @@ test_write_wifi_dynamic_wep_leap (void) /* IP6 setting */ s_ip6 = (NMSettingIP6Config *) nm_setting_ip6_config_new (); g_assert (s_ip6); - g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, NULL); nm_connection_add_setting (connection, NM_SETTING (s_ip6)); + g_object_set (s_ip6, + NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + NM_SETTING_IP6_CONFIG_MAY_FAIL, TRUE, + NULL); + success = nm_connection_verify (connection, &error); g_assert_no_error (error); g_assert (success); @@ -10688,6 +10763,7 @@ test_write_wired_qeth_dhcp (void) g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + NM_SETTING_IP6_CONFIG_MAY_FAIL, TRUE, NULL); /* Verify */ @@ -10804,7 +10880,11 @@ test_write_wired_ctc_dhcp (void) s_ip6 = (NMSettingIP6Config *) nm_setting_ip6_config_new (); g_assert (s_ip6); nm_connection_add_setting (connection, NM_SETTING (s_ip6)); - g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, NULL); + + g_object_set (s_ip6, + NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + NM_SETTING_IP6_CONFIG_MAY_FAIL, TRUE, + NULL); /* Verify */ success = nm_connection_verify (connection, &error); @@ -10939,6 +11019,7 @@ test_write_permissions (void) g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + NM_SETTING_IP6_CONFIG_MAY_FAIL, TRUE, NULL); /* Verify */ @@ -11040,7 +11121,11 @@ test_write_wifi_wep_agent_keys (void) s_ip6 = (NMSettingIP6Config *) nm_setting_ip6_config_new (); g_assert (s_ip6); nm_connection_add_setting (connection, NM_SETTING (s_ip6)); - g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, NULL); + + g_object_set (s_ip6, + NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + NM_SETTING_IP6_CONFIG_MAY_FAIL, TRUE, + NULL); /* Wifi setting */ s_wifi = (NMSettingWireless *) nm_setting_wireless_new (); diff --git a/src/settings/plugins/ifcfg-rh/writer.c b/src/settings/plugins/ifcfg-rh/writer.c index 6b66b232..192226ac 100644 --- a/src/settings/plugins/ifcfg-rh/writer.c +++ b/src/settings/plugins/ifcfg-rh/writer.c @@ -758,6 +758,7 @@ write_wireless_setting (NMConnection *connection, char buf[33]; guint32 mtu, chan, i; gboolean adhoc = FALSE, hex_ssid = FALSE; + const GSList *macaddr_blacklist; s_wireless = (NMSettingWireless *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS); if (!s_wireless) { @@ -786,6 +787,23 @@ write_wireless_setting (NMConnection *connection, g_free (tmp); } + svSetValue (ifcfg, "HWADDR_BLACKLIST", NULL, FALSE); + macaddr_blacklist = nm_setting_wireless_get_mac_address_blacklist (s_wireless); + if (macaddr_blacklist) { + const GSList *iter; + GString *blacklist_str = g_string_new (NULL); + + for (iter = macaddr_blacklist; iter; iter = g_slist_next (iter)) { + g_string_append (blacklist_str, iter->data); + g_string_append_c (blacklist_str, ' '); + + } + if (blacklist_str->len > 0) + g_string_truncate (blacklist_str, blacklist_str->len - 1); + svSetValue (ifcfg, "HWADDR_BLACKLIST", blacklist_str->str, FALSE); + g_string_free (blacklist_str, TRUE); + } + svSetValue (ifcfg, "MTU", NULL, FALSE); mtu = nm_setting_wireless_get_mtu (s_wireless); if (mtu) { @@ -934,6 +952,7 @@ write_wired_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) guint32 mtu, num_opts, i; const GPtrArray *s390_subchannels; GString *str; + const GSList *macaddr_blacklist; s_wired = (NMSettingWired *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRED); if (!s_wired) { @@ -961,6 +980,23 @@ write_wired_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) g_free (tmp); } + svSetValue (ifcfg, "HWADDR_BLACKLIST", NULL, FALSE); + macaddr_blacklist = nm_setting_wired_get_mac_address_blacklist (s_wired); + if (macaddr_blacklist) { + const GSList *iter; + GString *blacklist_str = g_string_new (NULL); + + for (iter = macaddr_blacklist; iter; iter = g_slist_next (iter)) { + g_string_append (blacklist_str, iter->data); + g_string_append_c (blacklist_str, ' '); + + } + if (blacklist_str->len > 0) + g_string_truncate (blacklist_str, blacklist_str->len - 1); + svSetValue (ifcfg, "HWADDR_BLACKLIST", blacklist_str->str, FALSE); + g_string_free (blacklist_str, TRUE); + } + svSetValue (ifcfg, "MTU", NULL, FALSE); mtu = nm_setting_wired_get_mtu (s_wired); if (mtu) { @@ -1316,13 +1352,14 @@ write_ip4_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) if (utils_has_route_file_new_syntax (route_path)) { shvarFile *routefile; - g_free (route_path); routefile = utils_get_route_ifcfg (ifcfg->fileName, TRUE); if (!routefile) { g_set_error (error, IFCFG_PLUGIN_ERROR, 0, - "Could not create route file '%s'", routefile->fileName); + "Could not create route file '%s'", route_path); + g_free (route_path); goto out; } + g_free (route_path); num = nm_setting_ip4_config_get_num_routes (s_ip4); for (i = 0; i < 256; i++) { diff --git a/src/settings/plugins/ifnet/plugin.c b/src/settings/plugins/ifnet/plugin.c index 69b7bc80..59083688 100644 --- a/src/settings/plugins/ifnet/plugin.c +++ b/src/settings/plugins/ifnet/plugin.c @@ -278,7 +278,8 @@ reload_connections (gpointer config) if (auto_refresh && is_true (auto_refresh)) { if (!nm_connection_compare (NM_CONNECTION (old), NM_CONNECTION (new), - NM_SETTING_COMPARE_FLAG_EXACT)) { + NM_SETTING_COMPARE_FLAG_IGNORE_AGENT_OWNED_SECRETS | + NM_SETTING_COMPARE_FLAG_IGNORE_NOT_SAVED_SECRETS)) { PLUGIN_PRINT (IFNET_PLUGIN_NAME, "Auto refreshing %s", conn_name); /* Remove and re-add to disconnect and reconnect with new settings */ diff --git a/src/settings/plugins/keyfile/common.h b/src/settings/plugins/keyfile/common.h index 6c8f9ceb..7d94a705 100644 --- a/src/settings/plugins/keyfile/common.h +++ b/src/settings/plugins/keyfile/common.h @@ -23,9 +23,6 @@ #include <glib.h> -#define SWP_TAG ".swp" -#define SWPX_TAG ".swpx" - #define KEYFILE_PLUGIN_NAME "keyfile" #define KEYFILE_PLUGIN_INFO "(c) 2007 - 2010 Red Hat, Inc. To report bugs please use the NetworkManager mailing list." diff --git a/src/settings/plugins/keyfile/plugin.c b/src/settings/plugins/keyfile/plugin.c index ffc614bc..af69c200 100644 --- a/src/settings/plugins/keyfile/plugin.c +++ b/src/settings/plugins/keyfile/plugin.c @@ -231,7 +231,8 @@ dir_changed (GFileMonitor *monitor, if (tmp) { if (!nm_connection_compare (NM_CONNECTION (connection), NM_CONNECTION (tmp), - NM_SETTING_COMPARE_FLAG_EXACT)) { + NM_SETTING_COMPARE_FLAG_IGNORE_AGENT_OWNED_SECRETS | + NM_SETTING_COMPARE_FLAG_IGNORE_NOT_SAVED_SECRETS)) { PLUGIN_PRINT (KEYFILE_PLUGIN_NAME, "updating %s", full_path); update_connection_settings (connection, tmp); } diff --git a/src/settings/plugins/keyfile/reader.c b/src/settings/plugins/keyfile/reader.c index a8eaaa8e..c4136e05 100644 --- a/src/settings/plugins/keyfile/reader.c +++ b/src/settings/plugins/keyfile/reader.c @@ -16,7 +16,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * Copyright (C) 2008 - 2009 Novell, Inc. - * Copyright (C) 2008 - 2010 Red Hat, Inc. + * Copyright (C) 2008 - 2011 Red Hat, Inc. */ #include <errno.h> @@ -40,6 +40,7 @@ #include <ctype.h> #include "nm-dbus-glib-types.h" +#include "nm-system-config-interface.h" #include "reader.h" #include "common.h" @@ -735,29 +736,27 @@ get_uchar_array (GKeyFile *keyfile, const char *key) { GByteArray *array = NULL; - char *p, *tmp_string; + char *tmp_string; gint *tmp_list; gsize length; int i; - /* New format: just a string. We try parsing the new format if there are - * no ';' in the string or it's not just numbers. + /* New format: just a string + * Old format: integer list; e.g. 11;25;38 */ - p = tmp_string = g_key_file_get_string (keyfile, setting_name, key, NULL); + tmp_string = g_key_file_get_string (keyfile, setting_name, key, NULL); if (tmp_string) { gboolean new_format = FALSE; + GRegex *regex; + GMatchInfo *match_info; + const char *pattern = "^[[:space:]]*[[:digit:]]{1,3}[[:space:]]*(;[[:space:]]*[[:digit:]]{1,3}[[:space:]]*)*(;[[:space:]]*)?$"; - if (strchr (p, ';') == NULL) - new_format = TRUE; - else { + regex = g_regex_new (pattern, 0, 0, NULL); + g_regex_match (regex, tmp_string, 0, &match_info); + if (!g_match_info_matches (match_info)) new_format = TRUE; - while (p && *p) { - if (!isdigit (*p++)) { - new_format = FALSE; - break; - } - } - } + g_match_info_free (match_info); + g_regex_unref (regex); if (new_format) { array = g_byte_array_sized_new (strlen (tmp_string)); @@ -835,6 +834,24 @@ get_cert_path (const char *keyfile_path, GByteArray *cert_path) #define SCHEME_PATH "file://" +static const char *certext[] = { ".pem", ".cert", ".crt", ".cer", ".p12", ".der", ".key" }; + +static gboolean +has_cert_ext (GByteArray *array) +{ + int i; + + for (i = 0; i < G_N_ELEMENTS (certext); i++) { + guint32 extlen = strlen (certext[i]); + + if (array->len <= extlen) + continue; + if (memcmp (&array->data[array->len - extlen], certext[i], extlen) == 0) + return TRUE; + } + return FALSE; +} + static void cert_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path) { @@ -859,17 +876,31 @@ cert_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const char && g_utf8_validate ((const char *) array->data, array->len, NULL)) { GByteArray *val; char *path; + gboolean exists; + + /* Might be a bare path without the file:// prefix; in that case + * if it's an absolute path, use that, otherwise treat it as a + * relative path to the current directory. + */ path = get_cert_path (keyfile_path, array); - if (g_file_test (path, G_FILE_TEST_EXISTS)) { + exists = g_file_test (path, G_FILE_TEST_EXISTS); + if ( exists + || memchr (array->data, '/', array->len) + || has_cert_ext (array)) { /* Construct the proper value as required for the PATH scheme */ val = g_byte_array_sized_new (strlen (SCHEME_PATH) + array->len + 1); g_byte_array_append (val, (const guint8 *) SCHEME_PATH, strlen (SCHEME_PATH)); - g_byte_array_append (val, array->data, array->len); + g_byte_array_append (val, (const guint8 *) path, strlen (path)); g_byte_array_append (val, (const guint8 *) "\0", 1); g_object_set (setting, key, val, NULL); g_byte_array_free (val, TRUE); success = TRUE; + + /* Warn if the certificate didn't exist */ + if (exists == FALSE) { + PLUGIN_WARN (KEYFILE_PLUGIN_NAME, " certificate or key %s does not exist", path); + } } g_free (path); } @@ -881,7 +912,7 @@ cert_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const char g_byte_array_free (array, TRUE); } else { - g_warning ("%s: ignoring invalid SSID for %s / %s", + g_warning ("%s: ignoring invalid key/cert value for %s / %s", __func__, setting_name, key); } } diff --git a/src/settings/plugins/keyfile/tests/keyfiles/Makefile.am b/src/settings/plugins/keyfile/tests/keyfiles/Makefile.am index 0ce03209..302db866 100644 --- a/src/settings/plugins/keyfile/tests/keyfiles/Makefile.am +++ b/src/settings/plugins/keyfile/tests/keyfiles/Makefile.am @@ -7,8 +7,11 @@ KEYFILES = \ ATT_Data_Connect_BT \ ATT_Data_Connect_Plain \ Test_String_SSID \ + Test_Intlist_SSID \ Test_Wired_TLS_Old \ - Test_Wired_TLS_New + Test_Wired_TLS_New \ + Test_Wired_TLS_Blob \ + Test_Wired_TLS_Path_Missing CERTS = \ test-ca-cert.pem \ diff --git a/src/settings/plugins/keyfile/tests/keyfiles/Makefile.in b/src/settings/plugins/keyfile/tests/keyfiles/Makefile.in index 2c3c1630..083615b0 100644 --- a/src/settings/plugins/keyfile/tests/keyfiles/Makefile.in +++ b/src/settings/plugins/keyfile/tests/keyfiles/Makefile.in @@ -277,8 +277,11 @@ KEYFILES = \ ATT_Data_Connect_BT \ ATT_Data_Connect_Plain \ Test_String_SSID \ + Test_Intlist_SSID \ Test_Wired_TLS_Old \ - Test_Wired_TLS_New + Test_Wired_TLS_New \ + Test_Wired_TLS_Blob \ + Test_Wired_TLS_Path_Missing CERTS = \ test-ca-cert.pem \ diff --git a/src/settings/plugins/keyfile/tests/keyfiles/Test_Intlist_SSID b/src/settings/plugins/keyfile/tests/keyfiles/Test_Intlist_SSID new file mode 100644 index 00000000..6d2bc0fa --- /dev/null +++ b/src/settings/plugins/keyfile/tests/keyfiles/Test_Intlist_SSID @@ -0,0 +1,11 @@ +[connection] +id=Test +uuid=2f962388-e5f3-45af-a62c-ac220b8f7baa +type=802-11-wireless + +[802-11-wireless] +ssid=98;108;97;104;49;50;51;52; + +[ipv4] +method=auto + diff --git a/src/settings/plugins/keyfile/tests/keyfiles/Test_Wired_TLS_Blob b/src/settings/plugins/keyfile/tests/keyfiles/Test_Wired_TLS_Blob new file mode 100644 index 00000000..9f4ef62f --- /dev/null +++ b/src/settings/plugins/keyfile/tests/keyfiles/Test_Wired_TLS_Blob @@ -0,0 +1,22 @@ + +[connection] +id=Wired TLS +uuid=5ee46013-9469-4c6a-a60a-0c7a1e1c7488 +type=802-3-ethernet + +[802-1x] +eap=tls; +identity=Bill Smith +ca-cert=48;130;2;52;48;130;1;161;2;16;2;173;102;126;78;69;254;94;87;111;60;152;25;94;221;192;48;13;6;9;42;134;72;134;247;13;1;1;2;5;0;48;95;49;11;48;9;6;3;85;4;6;19;2;85;83;49;32;48;30;6;3;85;4;10;19;23;82;83;65;32;68;97;116;97;32;83;101;99;117;114;105;116;121;44;32;73;110;99;46;49;46;48;44;6;3;85;4;11;19;37;83;101;99;117;114;101;32;83;101;114;118;101;114;32;67;101;114;116;105;102;105;99;97;116;105;111;110;32;65;117;116;104;111;114;105;116;121;48;30;23;13;57;52;49;49;48;57;48;48;48;48;48;48;90;23;13;49;48;48;49;48;55;50;51;53;57;53;57;90;48;95;49;11;48;9;6;3;85;4;6;19;2;85;83;49;32;48;30;6;3;85;4;10;19;23;82;83;65;32;68;97;116;97;32;83;101;99;117;114;105;116;121;44;32;73;110;99;46;49;46;48;44;6;3;85;4;11;19;37;83;101;99;117;114;101;32;83;101;114;118;101;114;32;67;101;114;116;105;102;105;99;97;116;105;111;110;32;65;117;116;104;111;114;105;116;121;48;129;155;48;13;6;9;42;134;72;134;247;13;1;1;1;5;0;3;129;137;0;48;129;133;2;126;0;146;206;122;193;174;131;62;90;170;137;131;87;172;37;1;118;12;173;174;142;44;55;206;235;53;120;100;84;3;229;132;64;81;201;191;143;8;226;138;130;8;210;22;134;55;85;233;177;33;2;173;118;104;129;154;5;162;75;201;75;37;102;34;86;108;136;7;143;247;129;89;109;132;7;101;112;19;113;118;62;155;119;76;227;80;137;86;152;72;185;29;167;41;26;19;46;74;17;89;156;30;21;213;73;84;44;115;58;105;130;177;151;57;156;109;112;103;72;229;221;45;214;200;30;123;2;3;1;0;1;48;13;6;9;42;134;72;134;247;13;1;1;2;5;0;3;126;0;101;221;126;225;178;236;176;226;58;224;236;113;70;154;25;17;184;211;199;160;180;3;64;38;2;62;9;156;225;18;179;209;90;246;55;165;183;97;3;182;91;22;105;59;198;68;8;12;136;83;12;107;151;73;199;62;53;220;108;185;187;170;223;92;187;58;47;147;96;182;169;75;77;242;32;247;205;95;127;100;123;142;220;0;92;215;250;119;202;57;22;89;111;14;234;211;181;131;127;77;77;66;86;118;180;201;95;4;248;56;248;235;210;95;117;95;205;123;252;229;142;128;124;252;80; +client-cert=102;105;108;101;58;47;47;47;104;111;109;101;47;100;99;98;119;47;68;101;115;107;116;111;112;47;99;101;114;116;105;110;102;114;97;47;99;108;105;101;110;116;46;112;101;109;0; +private-key=102;105;108;101;58;47;47;47;104;111;109;101;47;100;99;98;119;47;68;101;115;107;116;111;112;47;99;101;114;116;105;110;102;114;97;47;99;108;105;101;110;116;46;112;101;109;0; +private-key-password=12345testing + +[ipv4] +method=auto + +[802-3-ethernet] +duplex=full + +[ipv6] +method=ignore diff --git a/src/settings/plugins/keyfile/tests/keyfiles/Test_Wired_TLS_Path_Missing b/src/settings/plugins/keyfile/tests/keyfiles/Test_Wired_TLS_Path_Missing new file mode 100644 index 00000000..2b39538e --- /dev/null +++ b/src/settings/plugins/keyfile/tests/keyfiles/Test_Wired_TLS_Path_Missing @@ -0,0 +1,22 @@ + +[connection] +id=Wired TLS +uuid=5ee46013-9469-4c6a-a60a-0c7a1e1c7488 +type=802-3-ethernet + +[802-1x] +eap=tls; +identity=Bill Smith +ca-cert=/some/random/cert/path.pem +client-cert=test-key-and-cert.pem +private-key=test-key-and-cert.pem +private-key-password=12345testing + +[ipv4] +method=auto + +[802-3-ethernet] +duplex=full + +[ipv6] +method=ignore diff --git a/src/settings/plugins/keyfile/tests/test-keyfile.c b/src/settings/plugins/keyfile/tests/test-keyfile.c index bfe5aa43..3bbaaaec 100644 --- a/src/settings/plugins/keyfile/tests/test-keyfile.c +++ b/src/settings/plugins/keyfile/tests/test-keyfile.c @@ -1438,6 +1438,131 @@ test_write_string_ssid (void) g_object_unref (connection); } +#define TEST_INTLIST_SSID_FILE TEST_KEYFILES_DIR"/Test_Intlist_SSID" + +static void +test_read_intlist_ssid (void) +{ + NMConnection *connection; + NMSettingWireless *s_wifi; + GError *error = NULL; + gboolean success; + const GByteArray *array; + const char *expected_ssid = "blah1234"; + + connection = nm_keyfile_plugin_connection_from_file (TEST_INTLIST_SSID_FILE, &error); + g_assert_no_error (error); + g_assert (connection); + + success = nm_connection_verify (connection, &error); + g_assert_no_error (error); + g_assert (success); + + /* SSID */ + s_wifi = nm_connection_get_setting_wireless (connection); + g_assert (s_wifi); + + array = nm_setting_wireless_get_ssid (s_wifi); + g_assert (array != NULL); + g_assert_cmpint (array->len, ==, strlen (expected_ssid)); + g_assert_cmpint (memcmp (array->data, expected_ssid, strlen (expected_ssid)), ==, 0); + + g_object_unref (connection); +} + +static void +test_write_intlist_ssid (void) +{ + NMConnection *connection; + NMSettingConnection *s_con; + NMSettingWireless *s_wifi; + NMSettingIP4Config *s_ip4; + char *uuid, *testfile = NULL; + GByteArray *ssid; + unsigned char tmpssid[] = { 65, 49, 50, 51, 0, 50, 50 }; + gboolean success; + NMConnection *reread; + GError *error = NULL; + pid_t owner_grp; + uid_t owner_uid; + GKeyFile *keyfile; + gint *intlist; + gsize len = 0, i; + + connection = nm_connection_new (); + g_assert (connection); + + /* Connection setting */ + + s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ()); + g_assert (s_con); + nm_connection_add_setting (connection, NM_SETTING (s_con)); + + uuid = nm_utils_uuid_generate (); + g_object_set (s_con, + NM_SETTING_CONNECTION_ID, "Intlist SSID Test", + NM_SETTING_CONNECTION_UUID, uuid, + NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRELESS_SETTING_NAME, + NULL); + g_free (uuid); + + /* Wireless setting */ + s_wifi = NM_SETTING_WIRELESS (nm_setting_wireless_new ()); + g_assert (s_wifi); + nm_connection_add_setting (connection, NM_SETTING (s_wifi)); + + ssid = g_byte_array_sized_new (sizeof (tmpssid)); + g_byte_array_append (ssid, &tmpssid[0], sizeof (tmpssid)); + g_object_set (s_wifi, NM_SETTING_WIRELESS_SSID, ssid, NULL); + g_byte_array_free (ssid, TRUE); + + /* IP4 setting */ + s_ip4 = NM_SETTING_IP4_CONFIG (nm_setting_ip4_config_new ()); + g_assert (s_ip4); + nm_connection_add_setting (connection, NM_SETTING (s_ip4)); + g_object_set (s_ip4, NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL); + + /* Write out the connection */ + owner_uid = geteuid (); + owner_grp = getegid (); + success = nm_keyfile_plugin_write_test_connection (connection, TEST_SCRATCH_DIR, owner_uid, owner_grp, &testfile, &error); + g_assert_no_error (error); + g_assert (success); + g_assert (testfile != NULL); + + /* Ensure the SSID was written out as an int list */ + keyfile = g_key_file_new (); + success = g_key_file_load_from_file (keyfile, testfile, 0, &error); + g_assert_no_error (error); + g_assert (success); + + intlist = g_key_file_get_integer_list (keyfile, NM_SETTING_WIRELESS_SETTING_NAME, NM_SETTING_WIRELESS_SSID, &len, &error); + g_assert_no_error (error); + g_assert (intlist); + g_assert_cmpint (len, ==, sizeof (tmpssid)); + + for (i = 0; i < len; i++) + g_assert_cmpint (intlist[i], ==, tmpssid[i]); + g_free (intlist); + + g_key_file_free (keyfile); + + /* Read the connection back in and compare it to the one we just wrote out */ + reread = nm_keyfile_plugin_connection_from_file (testfile, &error); + g_assert_no_error (error); + g_assert (reread); + + success = nm_connection_compare (connection, reread, NM_SETTING_COMPARE_FLAG_EXACT); + g_assert (success); + + g_clear_error (&error); + unlink (testfile); + g_free (testfile); + + g_object_unref (reread); + g_object_unref (connection); +} + #define TEST_BT_DUN_FILE TEST_KEYFILES_DIR"/ATT_Data_Connect_BT" static void @@ -1953,6 +2078,133 @@ test_write_gsm_connection (void) g_object_unref (connection); } +#define TEST_WIRED_TLS_BLOB_FILE TEST_KEYFILES_DIR"/Test_Wired_TLS_Blob" + +static void +test_read_wired_8021x_tls_blob_connection (void) +{ + NMConnection *connection; + NMSetting *s_wired; + NMSetting8021x *s_8021x; + GError *error = NULL; + const char *tmp; + gboolean success; + const GByteArray *array; + + connection = nm_keyfile_plugin_connection_from_file (TEST_WIRED_TLS_BLOB_FILE, &error); + if (connection == NULL) { + g_assert (error); + g_warning ("Failed to read %s: %s", TEST_WIRED_TLS_BLOB_FILE, error->message); + g_assert (connection); + } + + success = nm_connection_verify (connection, &error); + if (!success) { + g_assert (error); + g_warning ("Failed to verify %s: %s", TEST_WIRED_TLS_BLOB_FILE, error->message); + g_assert (success); + } + + /* ===== Wired Setting ===== */ + s_wired = nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRED); + g_assert (s_wired != NULL); + + /* ===== 802.1x Setting ===== */ + s_8021x = (NMSetting8021x *) nm_connection_get_setting (connection, NM_TYPE_SETTING_802_1X); + g_assert (s_8021x != NULL); + + g_assert (nm_setting_802_1x_get_num_eap_methods (s_8021x) == 1); + tmp = nm_setting_802_1x_get_eap_method (s_8021x, 0); + g_assert (g_strcmp0 (tmp, "tls") == 0); + + tmp = nm_setting_802_1x_get_identity (s_8021x); + g_assert (g_strcmp0 (tmp, "Bill Smith") == 0); + + tmp = nm_setting_802_1x_get_private_key_password (s_8021x); + g_assert (g_strcmp0 (tmp, "12345testing") == 0); + + g_assert_cmpint (nm_setting_802_1x_get_ca_cert_scheme (s_8021x), ==, NM_SETTING_802_1X_CK_SCHEME_BLOB); + + /* Make sure it's not a path, since it's a blob */ + tmp = nm_setting_802_1x_get_ca_cert_path (s_8021x); + g_assert (tmp == NULL); + + /* Validate the path */ + array = nm_setting_802_1x_get_ca_cert_blob (s_8021x); + g_assert (array != NULL); + g_assert_cmpint (array->len, ==, 568); + + tmp = nm_setting_802_1x_get_client_cert_path (s_8021x); + g_assert_cmpstr (tmp, ==, "/home/dcbw/Desktop/certinfra/client.pem"); + + tmp = nm_setting_802_1x_get_private_key_path (s_8021x); + g_assert_cmpstr (tmp, ==, "/home/dcbw/Desktop/certinfra/client.pem"); + + g_object_unref (connection); +} + +#define TEST_WIRED_TLS_PATH_MISSING_FILE TEST_KEYFILES_DIR"/Test_Wired_TLS_Path_Missing" + +static void +test_read_wired_8021x_tls_bad_path_connection (void) +{ + NMConnection *connection; + NMSetting *s_wired; + NMSetting8021x *s_8021x; + GError *error = NULL; + const char *tmp; + char *tmp2; + gboolean success; + + connection = nm_keyfile_plugin_connection_from_file (TEST_WIRED_TLS_PATH_MISSING_FILE, &error); + if (connection == NULL) { + g_assert (error); + g_warning ("Failed to read %s: %s", TEST_WIRED_TLS_PATH_MISSING_FILE, error->message); + g_assert (connection); + } + + success = nm_connection_verify (connection, &error); + if (!success) { + g_assert (error); + g_warning ("Failed to verify %s: %s", TEST_WIRED_TLS_BLOB_FILE, error->message); + g_assert (success); + } + + /* ===== Wired Setting ===== */ + s_wired = nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRED); + g_assert (s_wired != NULL); + + /* ===== 802.1x Setting ===== */ + s_8021x = (NMSetting8021x *) nm_connection_get_setting (connection, NM_TYPE_SETTING_802_1X); + g_assert (s_8021x != NULL); + + g_assert (nm_setting_802_1x_get_num_eap_methods (s_8021x) == 1); + tmp = nm_setting_802_1x_get_eap_method (s_8021x, 0); + g_assert (g_strcmp0 (tmp, "tls") == 0); + + tmp = nm_setting_802_1x_get_identity (s_8021x); + g_assert (g_strcmp0 (tmp, "Bill Smith") == 0); + + tmp = nm_setting_802_1x_get_private_key_password (s_8021x); + g_assert (g_strcmp0 (tmp, "12345testing") == 0); + + g_assert_cmpint (nm_setting_802_1x_get_ca_cert_scheme (s_8021x), ==, NM_SETTING_802_1X_CK_SCHEME_PATH); + + tmp = nm_setting_802_1x_get_ca_cert_path (s_8021x); + g_assert_cmpstr (tmp, ==, "/some/random/cert/path.pem"); + + tmp2 = g_strdup_printf (TEST_KEYFILES_DIR "/test-key-and-cert.pem"); + + tmp = nm_setting_802_1x_get_client_cert_path (s_8021x); + g_assert_cmpstr (tmp, ==, tmp2); + + tmp = nm_setting_802_1x_get_private_key_path (s_8021x); + g_assert_cmpstr (tmp, ==, tmp2); + + g_free (tmp2); + g_object_unref (connection); +} + #define TEST_WIRED_TLS_OLD_FILE TEST_KEYFILES_DIR"/Test_Wired_TLS_Old" static void @@ -2019,6 +2271,7 @@ test_read_wired_8021x_tls_new_connection (void) NMSetting8021x *s_8021x; GError *error = NULL; const char *tmp; + char *tmp2; gboolean success; connection = nm_keyfile_plugin_connection_from_file (TEST_WIRED_TLS_NEW_FILE, &error); @@ -2053,15 +2306,20 @@ test_read_wired_8021x_tls_new_connection (void) tmp = nm_setting_802_1x_get_private_key_password (s_8021x); g_assert (g_strcmp0 (tmp, "12345testing") == 0); + tmp2 = g_strdup_printf (TEST_KEYFILES_DIR "/test-ca-cert.pem"); tmp = nm_setting_802_1x_get_ca_cert_path (s_8021x); - g_assert (g_strcmp0 (tmp, "test-ca-cert.pem") == 0); + g_assert_cmpstr (tmp, ==, tmp2); + g_free (tmp2); + + tmp2 = g_strdup_printf (TEST_KEYFILES_DIR "/test-key-and-cert.pem"); tmp = nm_setting_802_1x_get_client_cert_path (s_8021x); - g_assert (g_strcmp0 (tmp, "test-key-and-cert.pem") == 0); + g_assert_cmpstr (tmp, ==, tmp2); tmp = nm_setting_802_1x_get_private_key_path (s_8021x); - g_assert (g_strcmp0 (tmp, "test-key-and-cert.pem") == 0); + g_assert_cmpstr (tmp, ==, tmp2); + g_free (tmp2); g_object_unref (connection); } @@ -2153,16 +2411,23 @@ create_wired_tls_connection (NMSetting8021xCKScheme scheme) return connection; } +static char * +get_path (const char *file, gboolean relative) +{ + return relative ? g_path_get_basename (file) : g_strdup (file); +} + static void test_write_wired_8021x_tls_connection_path (void) { NMConnection *connection; - char *tmp; + char *tmp, *tmp2; gboolean success; NMConnection *reread; char *testfile = NULL; GError *error = NULL; GKeyFile *keyfile; + gboolean relative = FALSE; connection = create_wired_tls_connection (NM_SETTING_802_1X_CK_SCHEME_PATH); g_assert (connection != NULL); @@ -2200,12 +2465,22 @@ test_write_wired_8021x_tls_connection_path (void) g_assert (success); } + /* Depending on whether this test is being run from 'make check' or + * 'make distcheck' we might be using relative paths (check) or + * absolute ones (distcheck). + */ + tmp2 = g_path_get_dirname (testfile); + if (g_strcmp0 (tmp2, TEST_KEYFILES_DIR) == 0) + relative = TRUE; + /* CA cert */ tmp = g_key_file_get_string (keyfile, NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_CA_CERT, NULL); - g_assert (g_strcmp0 (tmp, TEST_WIRED_TLS_CA_CERT) == 0); + tmp2 = get_path (TEST_WIRED_TLS_CA_CERT, relative); + g_assert_cmpstr (tmp, ==, tmp2); + g_free (tmp2); g_free (tmp); /* Client cert */ @@ -2213,7 +2488,9 @@ test_write_wired_8021x_tls_connection_path (void) NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_CLIENT_CERT, NULL); - g_assert (g_strcmp0 (tmp, TEST_WIRED_TLS_CLIENT_CERT) == 0); + tmp2 = get_path (TEST_WIRED_TLS_CLIENT_CERT, relative); + g_assert_cmpstr (tmp, ==, tmp2); + g_free (tmp2); g_free (tmp); /* Private key */ @@ -2221,7 +2498,9 @@ test_write_wired_8021x_tls_connection_path (void) NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_PRIVATE_KEY, NULL); - g_assert (g_strcmp0 (tmp, TEST_WIRED_TLS_PRIVKEY) == 0); + tmp2 = get_path (TEST_WIRED_TLS_PRIVKEY, relative); + g_assert_cmpstr (tmp, ==, tmp2); + g_free (tmp2); g_free (tmp); g_key_file_free (keyfile); @@ -2334,12 +2613,18 @@ int main (int argc, char **argv) test_read_string_ssid (); test_write_string_ssid (); + test_read_intlist_ssid (); + test_write_intlist_ssid (); + test_read_bt_dun_connection (); test_write_bt_dun_connection (); test_read_gsm_connection (); test_write_gsm_connection (); + test_read_wired_8021x_tls_blob_connection (); + test_read_wired_8021x_tls_bad_path_connection (); + test_read_wired_8021x_tls_old_connection (); test_read_wired_8021x_tls_new_connection (); test_write_wired_8021x_tls_connection_path (); diff --git a/src/settings/plugins/keyfile/utils.c b/src/settings/plugins/keyfile/utils.c index 7b93a245..f3531172 100644 --- a/src/settings/plugins/keyfile/utils.c +++ b/src/settings/plugins/keyfile/utils.c @@ -74,6 +74,11 @@ check_suffix (const char *base, const char *tag) return FALSE; } +#define SWP_TAG ".swp" +#define SWPX_TAG ".swpx" +#define PEM_TAG ".pem" +#define DER_TAG ".der" + gboolean nm_keyfile_plugin_utils_should_ignore_file (const char *filename) { @@ -88,6 +93,8 @@ nm_keyfile_plugin_utils_should_ignore_file (const char *filename) /* Ignore files with certain patterns */ if ( (check_prefix (base, ".") && check_suffix (base, SWP_TAG)) /* vim temporary files: .filename.swp */ || (check_prefix (base, ".") && check_suffix (base, SWPX_TAG)) /* vim temporary files: .filename.swpx */ + || check_suffix (base, PEM_TAG) /* 802.1x certificates and keys */ + || check_suffix (base, DER_TAG) /* 802.1x certificates and keys */ || check_mkstemp_suffix (base) /* temporary files created by mkstemp() */ || base[strlen (base) - 1] == '~') ignore = TRUE; diff --git a/src/settings/plugins/keyfile/writer.c b/src/settings/plugins/keyfile/writer.c index eeb14556..060093ce 100644 --- a/src/settings/plugins/keyfile/writer.c +++ b/src/settings/plugins/keyfile/writer.c @@ -668,6 +668,16 @@ cert_writer (GKeyFile *file, if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) { path = objtype->path_func (NM_SETTING_802_1X (setting)); g_assert (path); + + /* If the path is rooted in the keyfile directory, just use a + * relative path instead of an absolute one. + */ + if (g_str_has_prefix (path, keyfile_dir)) { + path += strlen (keyfile_dir); + while (*path == '/') + path++; + } + g_key_file_set_string (file, setting_name, key, path); } else if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) { const GByteArray *blob; diff --git a/src/supplicant-manager/nm-supplicant-config.c b/src/supplicant-manager/nm-supplicant-config.c index 81d68bb3..c651e0fa 100644 --- a/src/supplicant-manager/nm-supplicant-config.c +++ b/src/supplicant-manager/nm-supplicant-config.c @@ -451,7 +451,7 @@ add_string_val (NMSupplicantConfig *self, return success; } -#define ADD_STRING_LIST_VAL(setting, setting_name, field, field_plural, name, ucase, secret) \ +#define ADD_STRING_LIST_VAL(setting, setting_name, field, field_plural, name, separator, ucase, secret) \ if (nm_setting_##setting_name##_get_num_##field_plural (setting)) { \ guint32 k; \ GString *str = g_string_new (NULL); \ @@ -460,7 +460,7 @@ add_string_val (NMSupplicantConfig *self, if (!str->len) { \ g_string_append (str, item); \ } else { \ - g_string_append_c (str, ' '); \ + g_string_append_c (str, separator); \ g_string_append (str, item); \ } \ } \ @@ -636,9 +636,9 @@ nm_supplicant_config_add_setting_wireless_security (NMSupplicantConfig *self, if ( !strcmp (key_mgmt, "wpa-none") || !strcmp (key_mgmt, "wpa-psk") || !strcmp (key_mgmt, "wpa-eap")) { - ADD_STRING_LIST_VAL (setting, wireless_security, proto, protos, "proto", TRUE, FALSE); - ADD_STRING_LIST_VAL (setting, wireless_security, pairwise, pairwise, "pairwise", TRUE, FALSE); - ADD_STRING_LIST_VAL (setting, wireless_security, group, groups, "group", TRUE, FALSE); + ADD_STRING_LIST_VAL (setting, wireless_security, proto, protos, "proto", ' ', TRUE, FALSE); + ADD_STRING_LIST_VAL (setting, wireless_security, pairwise, pairwise, "pairwise", ' ', TRUE, FALSE); + ADD_STRING_LIST_VAL (setting, wireless_security, group, groups, "group", ' ', TRUE, FALSE); } /* WEP keys if required */ @@ -742,7 +742,7 @@ nm_supplicant_config_add_setting_8021x (NMSupplicantConfig *self, nm_supplicant_config_set_ap_scan (self, 0); } - ADD_STRING_LIST_VAL (setting, 802_1x, eap_method, eap_methods, "eap", TRUE, FALSE); + ADD_STRING_LIST_VAL (setting, 802_1x, eap_method, eap_methods, "eap", ' ', TRUE, FALSE); /* Check for PEAP + GTC */ num_eap = nm_setting_802_1x_get_num_eap_methods (setting); @@ -864,6 +864,18 @@ nm_supplicant_config_add_setting_8021x (NMSupplicantConfig *self, break; } + /* Subject match */ + value = nm_setting_802_1x_get_subject_match (setting); + if (!add_string_val (self, value, "subject_match", FALSE, FALSE)) + return FALSE; + value = nm_setting_802_1x_get_phase2_subject_match (setting); + if (!add_string_val (self, value, "subject_match2", FALSE, FALSE)) + return FALSE; + + /* altSubjectName match */ + ADD_STRING_LIST_VAL (setting, 802_1x, altsubject_match, altsubject_matches, "altsubject_match", ';', FALSE, FALSE); + ADD_STRING_LIST_VAL (setting, 802_1x, phase2_altsubject_match, phase2_altsubject_matches, "altsubject_match2", ';', FALSE, FALSE); + /* Private key */ added = FALSE; switch (nm_setting_802_1x_get_private_key_scheme (setting)) { diff --git a/src/supplicant-manager/nm-supplicant-interface.c b/src/supplicant-manager/nm-supplicant-interface.c index e9e58f39..857cde51 100644 --- a/src/supplicant-manager/nm-supplicant-interface.c +++ b/src/supplicant-manager/nm-supplicant-interface.c @@ -467,7 +467,8 @@ interface_add_done (NMSupplicantInterface *self, char *path) G_CALLBACK (wpas_iface_properties_changed), self, NULL); - dbus_g_proxy_add_signal (priv->iface_proxy, "ScanDone", G_TYPE_INVALID); + dbus_g_proxy_add_signal (priv->iface_proxy, "ScanDone", + G_TYPE_BOOLEAN, G_TYPE_INVALID); dbus_g_proxy_connect_signal (priv->iface_proxy, "ScanDone", G_CALLBACK (wpas_iface_scan_done), self, @@ -475,9 +476,11 @@ interface_add_done (NMSupplicantInterface *self, char *path) dbus_g_object_register_marshaller (_nm_marshal_VOID__STRING_BOXED, G_TYPE_NONE, - G_TYPE_STRING, DBUS_TYPE_G_MAP_OF_VARIANT, + DBUS_TYPE_G_OBJECT_PATH, DBUS_TYPE_G_MAP_OF_VARIANT, G_TYPE_INVALID); - dbus_g_proxy_add_signal (priv->iface_proxy, "BSSAdded", G_TYPE_INVALID); + dbus_g_proxy_add_signal (priv->iface_proxy, "BSSAdded", + DBUS_TYPE_G_OBJECT_PATH, DBUS_TYPE_G_MAP_OF_VARIANT, + G_TYPE_INVALID); dbus_g_proxy_connect_signal (priv->iface_proxy, "BSSAdded", G_CALLBACK (wpas_iface_bss_added), self, @@ -551,6 +554,9 @@ interface_add_cb (DBusGProxy *proxy, /* Interface already added, just get its object path */ interface_get (info->interface); } else if ( g_error_matches (error, DBUS_GERROR, DBUS_GERROR_SERVICE_UNKNOWN) + || g_error_matches (error, DBUS_GERROR, DBUS_GERROR_SPAWN_EXEC_FAILED) + || g_error_matches (error, DBUS_GERROR, DBUS_GERROR_SPAWN_FORK_FAILED) + || g_error_matches (error, DBUS_GERROR, DBUS_GERROR_SPAWN_FAILED) || dbus_g_error_has_name (error, DBUS_ERROR_SPAWN_SERVICE_NOT_FOUND)) { /* Supplicant wasn't running and could be launched via service * activation. Wait for it to start by moving back to the INIT @@ -682,12 +688,13 @@ nm_supplicant_interface_disconnect (NMSupplicantInterface * self) if (!priv->iface_proxy) return; - /* Don't try to disconnect if the supplicant interface is already disconnected */ - if ( priv->state == NM_SUPPLICANT_INTERFACE_STATE_DISCONNECTED - || priv->state == NM_SUPPLICANT_INTERFACE_STATE_INACTIVE) { - g_free (priv->net_path); - priv->net_path = NULL; - return; + /* Disconnect from the current AP */ + if ( (priv->state >= NM_SUPPLICANT_INTERFACE_STATE_SCANNING) + && (priv->state <= NM_SUPPLICANT_INTERFACE_STATE_COMPLETED)) { + dbus_g_proxy_begin_call (priv->iface_proxy, "Disconnect", + disconnect_cb, + NULL, NULL, + G_TYPE_INVALID); } /* Remove any network that was added by NetworkManager */ @@ -700,11 +707,6 @@ nm_supplicant_interface_disconnect (NMSupplicantInterface * self) g_free (priv->net_path); priv->net_path = NULL; } - - dbus_g_proxy_begin_call (priv->iface_proxy, "Disconnect", - disconnect_cb, - NULL, NULL, - G_TYPE_INVALID); } static void diff --git a/src/supplicant-manager/nm-supplicant-settings-verify.c b/src/supplicant-manager/nm-supplicant-settings-verify.c index 0372fd93..aea5bfec 100644 --- a/src/supplicant-manager/nm-supplicant-settings-verify.c +++ b/src/supplicant-manager/nm-supplicant-settings-verify.c @@ -102,6 +102,8 @@ static const struct Opt opt_table[] = { { "identity", TYPE_BYTES, 0, 0, FALSE, NULL }, { "password", TYPE_BYTES, 0, 0, FALSE, NULL }, { "ca_path", TYPE_BYTES, 0, 0, FALSE, NULL }, + { "subject_match", TYPE_BYTES, 0, 0, FALSE, NULL }, + { "altsubject_match", TYPE_BYTES, 0, 0, FALSE, NULL }, { "ca_cert", TYPE_BYTES, 0, 65536, FALSE, NULL }, { "client_cert", TYPE_BYTES, 0, 65536, FALSE, NULL }, { "private_key", TYPE_BYTES, 0, 65536, FALSE, NULL }, @@ -110,6 +112,8 @@ static const struct Opt opt_table[] = { { "phase2", TYPE_KEYWORD, 0, 0, TRUE, phase2_allowed }, { "anonymous_identity", TYPE_BYTES, 0, 0, FALSE, NULL }, { "ca_path2", TYPE_BYTES, 0, 0, FALSE, NULL }, + { "subject_match2", TYPE_BYTES, 0, 0, FALSE, NULL }, + { "altsubject_match2", TYPE_BYTES, 0, 0, FALSE, NULL }, { "ca_cert2", TYPE_BYTES, 0, 65536, FALSE, NULL }, { "client_cert2", TYPE_BYTES, 0, 65536, FALSE, NULL }, { "private_key2", TYPE_BYTES, 0, 65536, FALSE, NULL }, diff --git a/src/supplicant-manager/tests/test-supplicant-config.c b/src/supplicant-manager/tests/test-supplicant-config.c index d0868b5f..d92fb0d1 100644 --- a/src/supplicant-manager/tests/test-supplicant-config.c +++ b/src/supplicant-manager/tests/test-supplicant-config.c @@ -15,7 +15,7 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * Copyright (C) 2008 - 2009 Red Hat, Inc. + * Copyright (C) 2008 - 2011 Red Hat, Inc. */ #include <stdio.h> @@ -496,11 +496,9 @@ test_wifi_wpa_psk_types (void) int main (int argc, char **argv) { GError *error = NULL; - DBusGConnection *bus; char *base; g_type_init (); - bus = dbus_g_bus_get (DBUS_BUS_SESSION, NULL); if (!nm_utils_init (&error)) FAIL ("nm-utils-init", "failed to initialize libnm-util: %s", error->message); diff --git a/src/tests/test-dhcp-options.c b/src/tests/test-dhcp-options.c index aa85f87e..9463ed5b 100644 --- a/src/tests/test-dhcp-options.c +++ b/src/tests/test-dhcp-options.c @@ -15,7 +15,7 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * Copyright (C) 2008 - 2010 Red Hat, Inc. + * Copyright (C) 2008 - 2011 Red Hat, Inc. * */ @@ -779,13 +779,11 @@ test_ip4_prefix_classless (const char *client) int main (int argc, char **argv) { GError *error = NULL; - DBusGConnection *bus; char *base; const char *clients[2][2] = { {DHCLIENT_PATH, "dhclient"}, {DHCPCD_PATH, "dhcpcd"} }; guint32 i; g_type_init (); - bus = dbus_g_bus_get (DBUS_BUS_SESSION, NULL); if (!nm_utils_init (&error)) FAIL ("nm-utils-init", "failed to initialize libnm-util: %s", error->message); diff --git a/src/vpn-manager/nm-vpn-connection.c b/src/vpn-manager/nm-vpn-connection.c index cf6135a6..067b2dce 100644 --- a/src/vpn-manager/nm-vpn-connection.c +++ b/src/vpn-manager/nm-vpn-connection.c @@ -53,6 +53,17 @@ G_DEFINE_TYPE (NMVPNConnection, nm_vpn_connection, NM_TYPE_VPN_CONNECTION_BASE) +typedef enum { + /* Only system secrets */ + SECRETS_REQ_SYSTEM = 0, + /* All existing secrets including agent secrets */ + SECRETS_REQ_EXISTING = 1, + /* New secrets required; ask an agent */ + SECRETS_REQ_NEW = 2, + /* Placeholder for bounds checking */ + SECRETS_REQ_LAST +} SecretsReq; + typedef struct { gboolean disposed; @@ -61,6 +72,7 @@ typedef struct { gboolean user_requested; gulong user_uid; guint32 secrets_id; + SecretsReq secrets_idx; char *username; NMDevice *parent_dev; @@ -98,6 +110,8 @@ enum { LAST_PROP }; +static void get_secrets (NMVPNConnection *self, SecretsReq secrets_idx); + static void nm_vpn_connection_set_vpn_state (NMVPNConnection *connection, NMVPNConnectionState vpn_state, @@ -792,84 +806,50 @@ nm_vpn_connection_disconnect (NMVPNConnection *connection, /******************************************************************************/ static void -vpn_secrets_cb (NMSettingsConnection *connection, - guint32 call_id, - const char *agent_username, - const char *setting_name, - GError *error, - gpointer user_data) -{ - NMVPNConnection *self = NM_VPN_CONNECTION (user_data); - NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self); - - g_return_if_fail (NM_CONNECTION (connection) == priv->connection); - g_return_if_fail (call_id == priv->secrets_id); - - priv->secrets_id = 0; - - if (error) - nm_vpn_connection_fail (self, NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS); - else - really_activate (self, agent_username); -} - -static void -connection_need_secrets_cb (DBusGProxy *proxy, - char *setting_name, - GError *error, - gpointer user_data) +plugin_need_secrets_cb (DBusGProxy *proxy, + char *setting_name, + GError *error, + gpointer user_data) { NMVPNConnection *self = NM_VPN_CONNECTION (user_data); NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self); - GError *local = NULL; if (error) { - nm_log_err (LOGD_VPN, "NeedSecrets failed: %s %s", + nm_log_err (LOGD_VPN, "(%s/%s) plugin NeedSecrets request #%d failed: %s %s", + nm_connection_get_uuid (priv->connection), + nm_connection_get_id (priv->connection), + priv->secrets_idx + 1, g_quark_to_string (error->domain), error->message); nm_vpn_connection_fail (self, NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS); return; } - if (!setting_name || !strlen (setting_name)) { - nm_log_dbg (LOGD_VPN, "(%s/%s) service indicated no additional secrets required", - nm_connection_get_uuid (priv->connection), - nm_connection_get_id (priv->connection)); + if (setting_name && strlen (setting_name)) { + /* More secrets required */ + nm_log_dbg (LOGD_VPN, "(%s/%s) service indicated additional secrets required", + nm_connection_get_uuid (priv->connection), + nm_connection_get_id (priv->connection)); - /* No secrets required */ - really_activate (self, priv->username); + get_secrets (self, priv->secrets_idx + 1); return; } - nm_log_dbg (LOGD_VPN, "(%s/%s) service indicated additional '%s' secrets required", - nm_connection_get_uuid (priv->connection), - nm_connection_get_id (priv->connection), - setting_name); + nm_log_dbg (LOGD_VPN, "(%s/%s) service indicated no additional secrets required", + nm_connection_get_uuid (priv->connection), + nm_connection_get_id (priv->connection)); - priv->secrets_id = nm_settings_connection_get_secrets (NM_SETTINGS_CONNECTION (priv->connection), - priv->user_requested, - priv->user_uid, - setting_name, - NM_SETTINGS_GET_SECRETS_FLAG_ALLOW_INTERACTION, - NULL, - vpn_secrets_cb, - self, - &local); - if (!priv->secrets_id) { - if (local) - nm_log_err (LOGD_VPN, "failed to get secrets: (%d) %s", local->code, local->message); - nm_vpn_connection_fail (self, NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS); - g_clear_error (&local); - } + /* No secrets required; we can start the VPN */ + really_activate (self, priv->username); } static void -existing_secrets_cb (NMSettingsConnection *connection, - guint32 call_id, - const char *agent_username, - const char *setting_name, - GError *error, - gpointer user_data) +get_secrets_cb (NMSettingsConnection *connection, + guint32 call_id, + const char *agent_username, + const char *setting_name, + GError *error, + gpointer user_data) { NMVPNConnection *self = NM_VPN_CONNECTION (user_data); NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self); @@ -881,57 +861,77 @@ existing_secrets_cb (NMSettingsConnection *connection, priv->secrets_id = 0; if (error) { - nm_log_err (LOGD_VPN, "Failed to request existing VPN secrets #2: (%s) %s", - g_quark_to_string (error->domain), - error->message); + nm_log_err (LOGD_VPN, "Failed to request VPN secrets #%d: (%d) %s", + priv->secrets_idx + 1, error->code, error->message); nm_vpn_connection_fail (self, NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS); } else { nm_log_dbg (LOGD_VPN, "(%s/%s) asking service if additional secrets are required", - nm_connection_get_uuid (priv->connection), - nm_connection_get_id (priv->connection)); + nm_connection_get_uuid (priv->connection), + nm_connection_get_id (priv->connection)); /* Cache the username for later */ - g_free (priv->username); - priv->username = g_strdup (agent_username); + if (agent_username) { + g_free (priv->username); + priv->username = g_strdup (agent_username); + } /* Ask the VPN service if more secrets are required */ hash = _hash_with_username (priv->connection, priv->username); org_freedesktop_NetworkManager_VPN_Plugin_need_secrets_async (priv->proxy, hash, - connection_need_secrets_cb, + plugin_need_secrets_cb, self); g_hash_table_destroy (hash); } } static void -get_existing_secrets (NMVPNConnection *self) +get_secrets (NMVPNConnection *self, SecretsReq secrets_idx) { NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self); + NMSettingsGetSecretsFlags flags = NM_SETTINGS_GET_SECRETS_FLAG_NONE; GError *error = NULL; + gboolean filter_by_uid = priv->user_requested; + + g_return_if_fail (secrets_idx < SECRETS_REQ_LAST); + priv->secrets_idx = secrets_idx; - nm_log_dbg (LOGD_VPN, "(%s/%s) requesting existing VPN secrets", + nm_log_dbg (LOGD_VPN, "(%s/%s) requesting VPN secrets pass #%d", nm_connection_get_uuid (priv->connection), - nm_connection_get_id (priv->connection)); + nm_connection_get_id (priv->connection), + priv->secrets_idx + 1); + + switch (priv->secrets_idx) { + case SECRETS_REQ_SYSTEM: + flags = NM_SETTINGS_GET_SECRETS_FLAG_ONLY_SYSTEM; + filter_by_uid = FALSE; + break; + case SECRETS_REQ_EXISTING: + flags = NM_SETTINGS_GET_SECRETS_FLAG_NONE; + break; + case SECRETS_REQ_NEW: + flags = NM_SETTINGS_GET_SECRETS_FLAG_ALLOW_INTERACTION; + break; + default: + g_assert_not_reached (); + } - /* Just get existing secrets if any so we can ask the VPN service if - * any more are required. - */ priv->secrets_id = nm_settings_connection_get_secrets (NM_SETTINGS_CONNECTION (priv->connection), - priv->user_requested, + filter_by_uid, priv->user_uid, NM_SETTING_VPN_SETTING_NAME, - NM_SETTINGS_GET_SECRETS_FLAG_NONE, + flags, NULL, - existing_secrets_cb, + get_secrets_cb, self, &error); - if (priv->secrets_id == 0) { - nm_log_err (LOGD_VPN, "Failed to request existing VPN secrets #1: (%s) %s", - g_quark_to_string (error->domain), - error->message); - g_error_free (error); + if (!priv->secrets_id) { + if (error) { + nm_log_err (LOGD_VPN, "failed to request VPN secrets #%d: (%d) %s", + priv->secrets_idx + 1, error->code, error->message); + } nm_vpn_connection_fail (self, NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS); + g_clear_error (&error); } } @@ -1002,10 +1002,16 @@ connection_state_changed (NMVPNConnection *self, nm_settings_connection_cancel_secrets (NM_SETTINGS_CONNECTION (priv->connection), priv->secrets_id); priv->secrets_id = 0; } + priv->secrets_idx = SECRETS_REQ_SYSTEM; switch (state) { case NM_VPN_CONNECTION_STATE_NEED_AUTH: - get_existing_secrets (self); + /* Kick off the secrets requests; first we get existing system secrets + * and ask the plugin if these are sufficient, next we get all existing + * secrets from system and from user agents and ask the plugin again, + * and last we ask the user for new secrets if required. + */ + get_secrets (self, SECRETS_REQ_SYSTEM); break; case NM_VPN_CONNECTION_STATE_ACTIVATED: /* Secrets no longer needed now that we're connected */ |