diff options
author | Lubomir Rintel <lkundrak@v3.sk> | 2017-04-28 19:09:17 +0200 |
---|---|---|
committer | Lubomir Rintel <lkundrak@v3.sk> | 2017-05-15 12:59:55 +0200 |
commit | 0eb76175047057fc466814632f25bfaf10566c37 (patch) | |
tree | 8e221769b3f1b9e788900b5b6b276aff614e4114 | |
parent | f87912326cad069939953f50d365b04c15fd41d7 (diff) | |
download | NetworkManager-0eb76175047057fc466814632f25bfaf10566c37.tar.gz |
wifi: attempt a WPS enroll when secrets are missing
If the supplicant returns the credentials, update the PSK in the
connection and disable further WPS enrollment attempts.
-rw-r--r-- | src/devices/wifi/nm-device-wifi.c | 151 |
1 files changed, 144 insertions, 7 deletions
diff --git a/src/devices/wifi/nm-device-wifi.c b/src/devices/wifi/nm-device-wifi.c index fd0cd0d9f3..79f8babc5d 100644 --- a/src/devices/wifi/nm-device-wifi.c +++ b/src/devices/wifi/nm-device-wifi.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) 2005 - 2012 Red Hat, Inc. + * Copyright (C) 2005 - 2017 Red Hat, Inc. * Copyright (C) 2006 - 2008 Novell, Inc. */ @@ -119,6 +119,8 @@ typedef struct { NMDeviceWifiCapabilities capabilities; gint32 hw_addr_scan_expire; + + guint wps_timeout_id; } NMDeviceWifiPrivate; struct _NMDeviceWifi @@ -169,6 +171,10 @@ static void supplicant_iface_scan_done_cb (NMSupplicantInterface * iface, gboolean success, NMDeviceWifi * self); +static void supplicant_iface_wps_credentials_cb (NMSupplicantInterface *iface, + GVariant *credentials, + NMDeviceWifi *self); + static void supplicant_iface_notify_scanning_cb (NMSupplicantInterface * iface, GParamSpec * pspec, NMDeviceWifi * self); @@ -268,6 +274,10 @@ supplicant_interface_acquire (NMDeviceWifi *self) G_CALLBACK (supplicant_iface_scan_done_cb), self); g_signal_connect (priv->sup_iface, + NM_SUPPLICANT_INTERFACE_WPS_CREDENTIALS, + G_CALLBACK (supplicant_iface_wps_credentials_cb), + self); + g_signal_connect (priv->sup_iface, "notify::"NM_SUPPLICANT_INTERFACE_SCANNING, G_CALLBACK (supplicant_iface_notify_scanning_cb), self); @@ -1747,6 +1757,7 @@ cleanup_association_attempt (NMDeviceWifi *self, gboolean disconnect) nm_clear_g_source (&priv->sup_timeout_id); nm_clear_g_source (&priv->link_timeout_id); + nm_clear_g_source (&priv->wps_timeout_id); if (disconnect && priv->sup_iface) nm_supplicant_interface_disconnect (priv->sup_iface); } @@ -1789,9 +1800,19 @@ wifi_secrets_cb (NMActRequest *req, if (error) { _LOGW (LOGD_WIFI, "%s", error->message); - nm_device_state_changed (device, - NM_DEVICE_STATE_FAILED, - NM_DEVICE_STATE_REASON_NO_SECRETS); + + if (g_error_matches (error, NM_AGENT_MANAGER_ERROR, + NM_AGENT_MANAGER_ERROR_USER_CANCELED)) { + /* Don't wait for WPS timeout on an explicit cancel. */ + nm_clear_g_source (&priv->wps_timeout_id); + } + + if (!priv->wps_timeout_id) { + /* Fail the device only if the WPS period is over too. */ + nm_device_state_changed (device, + NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_NO_SECRETS); + } } else nm_device_activate_schedule_stage1_device_prepare (device); } @@ -1807,6 +1828,73 @@ wifi_secrets_cancel (NMDeviceWifi *self) } static void +supplicant_iface_wps_credentials_cb (NMSupplicantInterface *iface, + GVariant *credentials, + NMDeviceWifi *self) +{ + NMActRequest *req; + GVariant *val, *secrets = NULL; + const char *array; + char psk[64]; + gsize psk_len = 0; + GError *error = NULL; + + if (nm_device_get_state (NM_DEVICE (self)) != NM_DEVICE_STATE_NEED_AUTH) { + _LOGI (LOGD_DEVICE | LOGD_WIFI, "WPS: The connection can't be updated with credentials"); + return; + } + + _LOGI (LOGD_DEVICE | LOGD_WIFI, "WPS: Updating the connection with credentials"); + + req = nm_device_get_act_request (NM_DEVICE (self)); + g_return_if_fail (NM_IS_ACT_REQUEST (req)); + + val = g_variant_lookup_value (credentials, "Key", G_VARIANT_TYPE_BYTESTRING); + if (val) { + array = g_variant_get_fixed_array (val, &psk_len, 1); + if (psk_len >= 8 || psk_len <= 63) { + memcpy (psk, array, psk_len); + psk[psk_len] = '\0'; + secrets = g_variant_new_parsed ("[{%s, [{%s, <%s>}]}]", + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + NM_SETTING_WIRELESS_SECURITY_PSK, psk); + } else + _LOGW (LOGD_DEVICE | LOGD_WIFI, "WPS: Ignoring a PSK of invalid length: %zd", psk_len); + g_variant_unref (val); + } + if (secrets) { + if (nm_settings_connection_new_secrets (nm_act_request_get_settings_connection (req), + nm_act_request_get_applied_connection (req), + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + secrets, &error)) { + wifi_secrets_cancel (self); + nm_device_activate_schedule_stage1_device_prepare (NM_DEVICE (self)); + } else { + _LOGW (LOGD_DEVICE | LOGD_WIFI, "WPS: Could not update the connection with credentials: %s", error->message); + g_error_free (error); + } + g_variant_unref (secrets); + } +} + +static gboolean +wps_timeout_cb (gpointer user_data) +{ + NMDeviceWifi *self = NM_DEVICE_WIFI (user_data); + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + + priv->wps_timeout_id = 0; + if (!priv->wifi_secrets_id) { + /* Fail only if the secrets are not being requested. */ + nm_device_state_changed (NM_DEVICE (self), + NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_NO_SECRETS); + } + + return G_SOURCE_REMOVE; +} + +static void wifi_secrets_get_secrets (NMDeviceWifi *self, const char *setting_name, NMSecretAgentGetSecretsFlags flags) @@ -2055,6 +2143,7 @@ supplicant_iface_state_cb (NMSupplicantInterface *iface, case NM_SUPPLICANT_INTERFACE_STATE_COMPLETED: nm_clear_g_source (&priv->sup_timeout_id); nm_clear_g_source (&priv->link_timeout_id); + nm_clear_g_source (&priv->wps_timeout_id); /* If this is the initial association during device activation, * schedule the next activation stage. @@ -2222,9 +2311,16 @@ handle_auth_or_fail (NMDeviceWifi *self, NMActRequest *req, gboolean new_secrets) { + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); const char *setting_name; guint32 tries; NMConnection *applied_connection; + NMSettingWirelessSecurity *s_wsec; + const char *bssid = NULL; + NM80211ApFlags ap_flags; + NMSettingWirelessSecurityWpsMethod wps_method; + const char *type; + NMSecretAgentGetSecretsFlags get_secret_flags = NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION; g_return_val_if_fail (NM_IS_DEVICE_WIFI (self), FALSE); @@ -2241,6 +2337,44 @@ handle_auth_or_fail (NMDeviceWifi *self, nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_NEED_AUTH, NM_DEVICE_STATE_REASON_NONE); + s_wsec = nm_connection_get_setting_wireless_security (applied_connection); + wps_method = nm_setting_wireless_security_get_wps_method (s_wsec); + + /* Negotiate the WPS method */ + if (wps_method == NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_DEFAULT) + wps_method = NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_AUTO; + + if ( wps_method & NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_AUTO + && priv->current_ap) { + /* Determine the method to use from AP capabilities. */ + ap_flags = nm_wifi_ap_get_flags (priv->current_ap); + if (ap_flags & NM_802_11_AP_FLAGS_WPS_PBC) + wps_method |= NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_PBC; + if (ap_flags & NM_802_11_AP_FLAGS_WPS_PIN) + wps_method |= NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_PIN; + if ( ap_flags & NM_802_11_AP_FLAGS_WPS + && wps_method == NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_AUTO) { + /* The AP doesn't specify which methods are supported. Allow all. */ + wps_method |= NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_PBC; + wps_method |= NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_PIN; + } + } + + if (wps_method & NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_PBC) { + get_secret_flags |= NM_SECRET_AGENT_GET_SECRETS_FLAG_WPS_PBC_ACTIVE; + type = "pbc"; + } else if (wps_method & NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_PIN) { + type = "pin"; + } else + type = NULL; + + if (type) { + priv->wps_timeout_id = g_timeout_add_seconds (30, wps_timeout_cb, self); + if (priv->current_ap) + bssid = nm_wifi_ap_get_address (priv->current_ap); + nm_supplicant_interface_enroll_wps (priv->sup_iface, type, bssid, NULL); + } + nm_act_request_clear_secrets (req); setting_name = nm_connection_need_secrets (applied_connection, NULL); if (!setting_name) { @@ -2248,9 +2382,9 @@ handle_auth_or_fail (NMDeviceWifi *self, return FALSE; } - wifi_secrets_get_secrets (self, setting_name, - NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION - | (new_secrets ? NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW : 0)); + if (new_secrets) + get_secret_flags |= NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW; + wifi_secrets_get_secrets (self, setting_name, get_secret_flags); g_object_set_qdata (G_OBJECT (applied_connection), wireless_secrets_tries_quark (), GUINT_TO_POINTER (++tries)); return TRUE; } @@ -2466,6 +2600,8 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason) s_wireless = nm_connection_get_setting_wireless (connection); g_return_val_if_fail (s_wireless, NM_ACT_STAGE_RETURN_FAILURE); + nm_supplicant_interface_cancel_wps (priv->sup_iface); + mode = nm_setting_wireless_get_mode (s_wireless); if (g_strcmp0 (mode, NM_SETTING_WIRELESS_MODE_INFRA) == 0) priv->mode = NM_802_11_MODE_INFRA; @@ -2614,6 +2750,7 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason) nm_clear_g_source (&priv->sup_timeout_id); nm_clear_g_source (&priv->link_timeout_id); + nm_clear_g_source (&priv->wps_timeout_id); req = nm_device_get_act_request (device); g_return_val_if_fail (req, NM_ACT_STAGE_RETURN_FAILURE); |