diff --git a/ChangeLog b/ChangeLog index e24e06566..6203a6d77 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,23 @@ +2006-08-10 Dan Williams + + Patch from Valentine Sinitsyn + * src/nm-ap-security.c + src/nm-ap-security.h + - Add authentication_required bits for subclasses to specify whether + or not real authentication is required for connections, i.e. whether + the AP rejects us when an encryption key is wrong or not. + + * src/nm-ap-security-wep.c + src/nm-ap-security-wpa-eap.c + src/nm-ap-security-wpa-psk.c + src/nm-ap-security-leap.c + - Implement authentication_required appropriately for each method + + * src/nm-device-802-11-wireless.c + - Be smarter about when to request a key; for example, using a wrong key + in WEP shared key mode previously just timed out and did not request + a new key + 2006-08-13 Dan Williams * gnome/libnm_glib/libnm_glib.c diff --git a/src/nm-ap-security-leap.c b/src/nm-ap-security-leap.c index 5a7bf9e77..1933d6233 100644 --- a/src/nm-ap-security-leap.c +++ b/src/nm-ap-security-leap.c @@ -152,6 +152,13 @@ real_get_default_capabilities (NMAPSecurity *instance) return caps; } +static gboolean +real_get_authentication_required (NMAPSecurity *instance) +{ + /* LEAP always requires authentication. */ + return TRUE; +} + static NMAPSecurity * real_copy_constructor (NMAPSecurity *instance) { @@ -186,6 +193,7 @@ nm_ap_security_leap_class_init (NMAPSecurityLEAPClass *klass) par_class->serialize_func = real_serialize; par_class->write_supplicant_config_func = real_write_supplicant_config; par_class->get_default_capabilities_func = real_get_default_capabilities; + par_class->get_authentication_required_func = real_get_authentication_required; g_type_class_add_private (object_class, sizeof (NMAPSecurityLEAPPrivate)); } diff --git a/src/nm-ap-security-wep.c b/src/nm-ap-security-wep.c index a7f3c4c59..fcd806b3d 100644 --- a/src/nm-ap-security-wep.c +++ b/src/nm-ap-security-wep.c @@ -174,6 +174,13 @@ real_get_default_capabilities (NMAPSecurity *instance) return caps; } +static gboolean +real_get_authentication_required (NMAPSecurity *instance) +{ + /* WEP really requires authentication in Shared mode only */ + return (get_auth_algorithm (NM_AP_SECURITY_WEP (instance)) == IW_AUTH_ALG_SHARED_KEY); +} + static NMAPSecurity * real_copy_constructor (NMAPSecurity *instance) { @@ -202,6 +209,7 @@ nm_ap_security_wep_class_init (NMAPSecurityWEPClass *klass) par_class->serialize_func = real_serialize; par_class->write_supplicant_config_func = real_write_supplicant_config; par_class->get_default_capabilities_func = real_get_default_capabilities; + par_class->get_authentication_required_func = real_get_authentication_required; g_type_class_add_private (object_class, sizeof (NMAPSecurityWEPPrivate)); } diff --git a/src/nm-ap-security-wpa-eap.c b/src/nm-ap-security-wpa-eap.c index 8cc49a775..26b42a67e 100644 --- a/src/nm-ap-security-wpa-eap.c +++ b/src/nm-ap-security-wpa-eap.c @@ -321,6 +321,13 @@ real_get_default_capabilities (NMAPSecurity *instance) return caps; } +static gboolean +real_get_authentication_required (NMAPSecurity *instance) +{ + /* WPA Enterprise is all about strong security */ + return TRUE; +} + static NMAPSecurity * real_copy_constructor (NMAPSecurity *instance) { @@ -372,6 +379,7 @@ nm_ap_security_wpa_eap_class_init (NMAPSecurityWPA_EAPClass *klass) par_class->serialize_func = real_serialize; par_class->write_supplicant_config_func = real_write_supplicant_config; par_class->get_default_capabilities_func = real_get_default_capabilities; + par_class->get_authentication_required_func = real_get_authentication_required; g_type_class_add_private (object_class, sizeof (NMAPSecurityWPA_EAPPrivate)); } diff --git a/src/nm-ap-security-wpa-psk.c b/src/nm-ap-security-wpa-psk.c index 61f81a44a..48ca76d2c 100644 --- a/src/nm-ap-security-wpa-psk.c +++ b/src/nm-ap-security-wpa-psk.c @@ -234,6 +234,13 @@ real_get_default_capabilities (NMAPSecurity *instance) return caps; } +static gboolean +real_get_authentication_required (NMAPSecurity *instance) +{ + /* WPA Personal always requires authentication in the infrastructure mode. */ + return TRUE; +} + static NMAPSecurity * real_copy_constructor (NMAPSecurity *instance) { @@ -264,6 +271,7 @@ nm_ap_security_wpa_psk_class_init (NMAPSecurityWPA_PSKClass *klass) par_class->serialize_func = real_serialize; par_class->write_supplicant_config_func = real_write_supplicant_config; par_class->get_default_capabilities_func = real_get_default_capabilities; + par_class->get_authentication_required_func = real_get_authentication_required; g_type_class_add_private (object_class, sizeof (NMAPSecurityWPA_PSKPrivate)); } diff --git a/src/nm-ap-security.c b/src/nm-ap-security.c index 0044d3e81..043ee03d6 100644 --- a/src/nm-ap-security.c +++ b/src/nm-ap-security.c @@ -158,6 +158,14 @@ nm_ap_security_get_default_capabilities (NMAPSecurity *self) return NM_AP_SECURITY_GET_CLASS (self)->get_default_capabilities_func (self); } +gboolean +nm_ap_security_get_authentication_required (NMAPSecurity *self) +{ + g_return_val_if_fail (self != NULL, FALSE); + + return NM_AP_SECURITY_GET_CLASS (self)->get_authentication_required_func (self); +} + gboolean nm_ap_security_write_supplicant_config (NMAPSecurity *self, diff --git a/src/nm-ap-security.h b/src/nm-ap-security.h index 100c7336e..3765a0b34 100644 --- a/src/nm-ap-security.h +++ b/src/nm-ap-security.h @@ -70,6 +70,7 @@ struct _NMAPSecurityClass gboolean adhoc); guint32 (*get_default_capabilities_func)(NMAPSecurity *self); + gboolean (*get_authentication_required_func)(NMAPSecurity *self); }; @@ -99,6 +100,8 @@ const char * nm_ap_security_get_description (NMAPSecurity *self); guint32 nm_ap_security_get_default_capabilities (NMAPSecurity *self); +gboolean nm_ap_security_get_authentication_required (NMAPSecurity *self); + G_END_DECLS #endif /* NM_AP_SECURITY_H */ diff --git a/src/nm-device-802-11-wireless.c b/src/nm-device-802-11-wireless.c index 102bbfe0a..6cd5fa4d0 100644 --- a/src/nm-device-802-11-wireless.c +++ b/src/nm-device-802-11-wireless.c @@ -2333,6 +2333,62 @@ ap_need_key (NMDevice80211Wireless *self, } +/* + * ap_is_auth_required + * + * Checks whether or not there is an encryption key present for + * this connection, and whether or not the authentication method + * in use will result in an authentication rejection if the key + * is wrong. For example, Ad Hoc mode networks don't have a + * master node and therefore nothing exists to reject the station. + * Similarly, Open System WEP access points don't reject a station + * when the key is wrong. Shared Key WEP access points will. + * + * Theory of operation here is that if: + * (a) the NMAPSecurity object specifies that authentication is + * required, and the AP rejects our authentication attempt during + * connection (which shows up as a wpa_supplicant disconnection + * event); or + * (b) the NMAPSecurity object specifies that no authentiation is + * required, and either DHCP times out or wpa_supplicant times out; + * + * then we need a new key from the user because our currenty key + * and/or authentication method is likely wrong. + * + */ +static gboolean +ap_is_auth_required (NMAccessPoint *ap, gboolean *has_key) +{ + NMAPSecurity *security; + int we_cipher; + gboolean auth_required = FALSE; + + g_return_val_if_fail (ap != NULL, FALSE); + g_return_val_if_fail (has_key != NULL, FALSE); + + *has_key = FALSE; + + /* Ad Hoc mode doesn't have any master station to validate + * security credentials, so no auth can possibly be required. + */ + if (nm_ap_get_mode(ap) == IW_MODE_ADHOC) + return FALSE; + + /* No encryption obviously means no possiblity of auth + * rejection due to a wrong encryption key. + */ + security = nm_ap_get_security (ap); + we_cipher = nm_ap_security_get_we_cipher (security); + if (we_cipher == IW_AUTH_CIPHER_NONE) + return FALSE; + + auth_required = nm_ap_security_get_authentication_required (security); + *has_key = TRUE; + + return auth_required; +} + + /****************************************************************************/ /* WPA Supplicant control stuff * @@ -2459,15 +2515,42 @@ supplicant_watch_cb (GPid pid, } +/* + * link_timeout_cb + * + * Called when the link to the access point has been down for a specified + * period of time. + */ static gboolean link_timeout_cb (gpointer user_data) { - NMDevice * dev = NM_DEVICE (user_data); + NMDevice * dev = NM_DEVICE (user_data); + NMDevice80211Wireless * self = NM_DEVICE_802_11_WIRELESS (user_data); + NMActRequest * req = nm_device_get_act_request (dev); + NMAccessPoint * ap = nm_act_request_get_ap (req); + NMData * data = nm_device_get_app_data (dev); + gboolean has_key; g_assert (dev); - nm_info ("%s: link timed out.", nm_device_get_iface (dev)); - nm_device_set_active_link (dev, FALSE); + /* Disconnect event during initial authentication and credentials + * ARE checked - we are likely to have wrong key. Ask the user for + * another one. + */ + if ( (nm_act_request_get_stage (req) == NM_ACT_STAGE_DEVICE_CONFIG) + && (ap_is_auth_required (ap, &has_key) && has_key)) + { + /* Association/authentication failed, we must have bad encryption key */ + nm_info ("Activation (%s/wireless): disconnected during association," + " asking for new key.", nm_device_get_iface (dev)); + supplicant_remove_timeout(self); + nm_dbus_get_user_key_for_network (data->dbus_connection, req, TRUE); + } + else + { + nm_info ("%s: link timed out.", nm_device_get_iface (dev)); + nm_device_set_active_link (dev, FALSE); + } return FALSE; } @@ -2561,19 +2644,43 @@ get_supplicant_timeout (NMDevice80211Wireless *self) } +/* + * supplicant_timeout_cb + * + * Called when the supplicant has been unable to connect to an access point + * within a specified period of time. + */ static gboolean supplicant_timeout_cb (gpointer user_data) { NMDevice * dev = NM_DEVICE (user_data); NMDevice80211Wireless * self = NM_DEVICE_802_11_WIRELESS (user_data); + NMActRequest * req = nm_device_get_act_request (dev); + NMAccessPoint * ap = nm_act_request_get_ap (req); + NMData * data = nm_device_get_app_data (dev); + gboolean has_key; g_assert (self); - - nm_info ("Activation (%s/wireless): association took too long (>%us), failing activation.", - nm_device_get_iface (dev), get_supplicant_timeout (self)); - - if (nm_device_is_activating (dev)) - nm_policy_schedule_activation_failed (nm_device_get_act_request (dev)); + + /* Timed out waiting for authentication success; if the security method + * in use does not require access point side authentication (Open System + * WEP, for example) then we are likely using the wrong authentication + * algorithm or key. Request new one from the user. + */ + if (!ap_is_auth_required (ap, &has_key) && has_key) + { + /* Activation failed, we must have bad encryption key */ + nm_info ("Activation (%s/wireless): association took too long (>%us), asking for new key.", + nm_device_get_iface (dev), get_supplicant_timeout (self)); + nm_dbus_get_user_key_for_network (data->dbus_connection, req, TRUE); + } + else + { + nm_info ("Activation (%s/wireless): association took too long (>%us), failing activation.", + nm_device_get_iface (dev), get_supplicant_timeout (self)); + if (nm_device_is_activating (dev)) + nm_policy_schedule_activation_failed (nm_device_get_act_request (dev)); + } return FALSE; } @@ -3034,6 +3141,7 @@ real_act_stage4_ip_config_timeout (NMDevice *dev, NMIP4Config * real_config = NULL; NMAPSecurity * security; NMData * data; + gboolean has_key; g_return_val_if_fail (config != NULL, NM_ACT_STAGE_RETURN_FAILURE); g_return_val_if_fail (*config == NULL, NM_ACT_STAGE_RETURN_FAILURE); @@ -3046,10 +3154,13 @@ real_act_stage4_ip_config_timeout (NMDevice *dev, security = nm_ap_get_security (ap); g_assert (security); - /* FIXME: should we only ask for a new key if the activation request is user-requested? */ - if (nm_ap_security_get_we_cipher (security) != IW_AUTH_CIPHER_NONE) + /* If the security credentials' validity was not checked by any + * peer during authentication process, and DHCP times out, then + * the encryption key is likely wrong. Ask the user for a new one. + */ + if (!ap_is_auth_required (ap, &has_key) && has_key) { - /* Activation failed, we must have bad WEP key */ + /* Activation failed, we must have bad encryption key */ nm_debug ("Activation (%s/wireless): could not get IP configuration info for '%s', asking for new key.", nm_device_get_iface (dev), nm_ap_get_essid (ap) ? nm_ap_get_essid (ap) : "(none)"); nm_dbus_get_user_key_for_network (data->dbus_connection, req, TRUE);