summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/devices/wifi/nm-device-wifi.c151
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);