diff options
Diffstat (limited to 'src/core/devices/wifi')
| -rw-r--r-- | src/core/devices/wifi/nm-device-iwd.c | 33 | ||||
| -rw-r--r-- | src/core/devices/wifi/nm-device-wifi.c | 174 | ||||
| -rw-r--r-- | src/core/devices/wifi/nm-wifi-ap.c | 18 | ||||
| -rw-r--r-- | src/core/devices/wifi/nm-wifi-utils.c | 40 | ||||
| -rw-r--r-- | src/core/devices/wifi/nm-wifi-utils.h | 4 | ||||
| -rw-r--r-- | src/core/devices/wifi/tests/test-devices-wifi.c | 46 |
6 files changed, 268 insertions, 47 deletions
diff --git a/src/core/devices/wifi/nm-device-iwd.c b/src/core/devices/wifi/nm-device-iwd.c index fa6e2f9d..94b9a7d7 100644 --- a/src/core/devices/wifi/nm-device-iwd.c +++ b/src/core/devices/wifi/nm-device-iwd.c @@ -2270,6 +2270,37 @@ add_new: return NM_ACT_STAGE_RETURN_SUCCESS; } +static void +set_powersave(NMDevice *device) +{ + NMDeviceIwd *self = NM_DEVICE_IWD(device); + NMSettingWireless *s_wireless; + NMSettingWirelessPowersave val; + + s_wireless = nm_device_get_applied_setting(device, NM_TYPE_SETTING_WIRELESS); + + g_return_if_fail(s_wireless); + + val = nm_setting_wireless_get_powersave(s_wireless); + if (val == NM_SETTING_WIRELESS_POWERSAVE_DEFAULT) { + val = nm_config_data_get_connection_default_int64(NM_CONFIG_GET_DATA, + "wifi.powersave", + device, + NM_SETTING_WIRELESS_POWERSAVE_IGNORE, + NM_SETTING_WIRELESS_POWERSAVE_ENABLE, + NM_SETTING_WIRELESS_POWERSAVE_IGNORE); + } + + _LOGT(LOGD_WIFI, "powersave is set to %u", (unsigned) val); + + if (val == NM_SETTING_WIRELESS_POWERSAVE_IGNORE) + return; + + nm_platform_wifi_set_powersave(nm_device_get_platform(device), + nm_device_get_ifindex(device), + val == NM_SETTING_WIRELESS_POWERSAVE_ENABLE); +} + static NMActStageReturn act_stage2_config(NMDevice *device, NMDeviceStateReason *out_failure_reason) { @@ -2297,6 +2328,8 @@ act_stage2_config(NMDevice *device, NMDeviceStateReason *out_failure_reason) goto out_fail; } + set_powersave(device); + /* With priv->iwd_autoconnect we have to let IWD handle retries for * infrastructure networks. IWD will not necessarily retry the same * network after a failure but it will likely go into an autoconnect diff --git a/src/core/devices/wifi/nm-device-wifi.c b/src/core/devices/wifi/nm-device-wifi.c index b41ed5e1..b836c1e8 100644 --- a/src/core/devices/wifi/nm-device-wifi.c +++ b/src/core/devices/wifi/nm-device-wifi.c @@ -194,6 +194,9 @@ static void supplicant_iface_notify_p2p_available(NMSupplicantInterface *iface, static void supplicant_iface_notify_wpa_psk_mismatch_cb(NMSupplicantInterface *iface, NMDeviceWifi *self); +static void supplicant_iface_notify_wpa_sae_mismatch_cb(NMSupplicantInterface *iface, + NMDeviceWifi *self); + static void periodic_update(NMDeviceWifi *self); static void ap_add_remove(NMDeviceWifi *self, @@ -631,6 +634,10 @@ supplicant_interface_acquire_cb(NMSupplicantManager *supplicant_manager, NM_SUPPLICANT_INTERFACE_PSK_MISMATCH, G_CALLBACK(supplicant_iface_notify_wpa_psk_mismatch_cb), self); + g_signal_connect(priv->sup_iface, + NM_SUPPLICANT_INTERFACE_SAE_MISMATCH, + G_CALLBACK(supplicant_iface_notify_wpa_sae_mismatch_cb), + self); _scan_notify_is_scanning(self); @@ -2191,21 +2198,16 @@ supplicant_iface_wps_credentials_cb(NMSupplicantInterface *iface, val_key = g_variant_lookup_value(credentials, "Key", G_VARIANT_TYPE_BYTESTRING); if (val_key) { - char psk[64]; + char psk[65]; array = g_variant_get_fixed_array(val_key, &psk_len, 1); - if (psk_len >= 8 && psk_len <= 63) { - memcpy(psk, array, psk_len); - psk[psk_len] = '\0'; - if (g_utf8_validate(psk, psk_len, NULL)) { - secrets = g_variant_new_parsed("[{%s, [{%s, <%s>}]}]", - NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, - NM_SETTING_WIRELESS_SECURITY_PSK, - psk); - g_variant_ref_sink(secrets); - } - } - if (!secrets) + if (nm_wifi_utils_wps_key_to_psk((const guint8 *) array, psk_len, &psk)) { + secrets = g_variant_new_parsed("[{%s, [{%s, <%s>}]}]", + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + NM_SETTING_WIRELESS_SECURITY_PSK, + psk); + g_variant_ref_sink(secrets); + } else _LOGW(LOGD_DEVICE | LOGD_WIFI, "WPS: ignore invalid PSK"); } @@ -2244,6 +2246,26 @@ wps_timeout_cb(gpointer user_data) return G_SOURCE_REMOVE; } +static gboolean +wifi_connection_is_new(NMDeviceWifi *self) +{ + NMDevice *device = NM_DEVICE(self); + NMActRequest *req; + NMSettingsConnection *connection; + guint64 timestamp = 0; + + req = nm_device_get_act_request(device); + g_return_val_if_fail(NM_IS_ACT_REQUEST(req), TRUE); + + connection = nm_act_request_get_settings_connection(req); + g_return_val_if_fail(NM_IS_SETTINGS_CONNECTION(connection), TRUE); + + if (nm_settings_connection_get_timestamp(connection, ×tamp) && timestamp != 0) + return FALSE; + + return TRUE; +} + static void wifi_secrets_get_secrets(NMDeviceWifi *self, const char *setting_name, @@ -2398,10 +2420,11 @@ handle_8021x_or_psk_auth_fail(NMDeviceWifi *self, NMSupplicantInterfaceState old_state, int disconnect_reason) { - NMDevice *device = NM_DEVICE(self); - NMActRequest *req; - const char *setting_name = NULL; - gboolean handled = FALSE; + NMDevice *device = NM_DEVICE(self); + NMActRequest *req; + const char *setting_name = NULL; + NMSecretAgentGetSecretsFlags secret_flags = NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION + | NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW; g_return_val_if_fail(new_state == NM_SUPPLICANT_INTERFACE_STATE_DISCONNECTED, FALSE); @@ -2411,8 +2434,7 @@ handle_8021x_or_psk_auth_fail(NMDeviceWifi *self, req = nm_device_get_act_request(NM_DEVICE(self)); g_return_val_if_fail(req != NULL, FALSE); - if (need_new_8021x_secrets(self, old_state, &setting_name) - || need_new_wpa_psk(self, old_state, disconnect_reason, &setting_name)) { + if (need_new_8021x_secrets(self, old_state, &setting_name)) { nm_act_request_clear_secrets(req); _LOGI(LOGD_DEVICE | LOGD_WIFI, @@ -2422,14 +2444,54 @@ handle_8021x_or_psk_auth_fail(NMDeviceWifi *self, nm_device_state_changed(device, NM_DEVICE_STATE_NEED_AUTH, NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT); - wifi_secrets_get_secrets(self, - setting_name, - NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION - | NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW); - handled = TRUE; + wifi_secrets_get_secrets(self, setting_name, secret_flags); + return TRUE; + } + + if (need_new_wpa_psk(self, old_state, disconnect_reason, &setting_name)) { + nm_act_request_clear_secrets(req); + cleanup_association_attempt(self, TRUE); + + if (wifi_connection_is_new(self)) { + _LOGI(LOGD_DEVICE | LOGD_WIFI, + "Activation: (wifi) new connection disconnected during association, asking for " + "new key"); + nm_device_state_changed(device, + NM_DEVICE_STATE_NEED_AUTH, + NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT); + wifi_secrets_get_secrets(self, setting_name, secret_flags); + return TRUE; + } + + if (!nm_device_auth_retries_try_next(device)) { + nm_device_state_changed(device, + NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_NO_SECRETS); + return TRUE; + } + + if (nm_device_auth_retries_has_next(device)) { + secret_flags &= ~NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW; + _LOGI( + LOGD_DEVICE | LOGD_WIFI, + "Activation: (wifi) disconnected during association, reauthenticating connection"); + } else { + _LOGI(LOGD_DEVICE | LOGD_WIFI, + "Activation: (wifi) disconnected during association, asking for new key"); + } + + nm_device_state_changed(device, + NM_DEVICE_STATE_NEED_AUTH, + NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT); + wifi_secrets_get_secrets(self, setting_name, secret_flags); + + return TRUE; } - return handled; + _LOGI(LOGD_DEVICE | LOGD_WIFI, + "Activation: (wifi) disconnected during association, retrying connection"); + + return FALSE; } static gboolean @@ -2861,6 +2923,12 @@ supplicant_iface_notify_wpa_psk_mismatch_cb(NMSupplicantInterface *iface, NMDevi if (nm_device_get_state(device) != NM_DEVICE_STATE_CONFIG) return; + if (!wifi_connection_is_new(self) && nm_device_auth_retries_has_next(device)) { + _LOGI(LOGD_DEVICE | LOGD_WIFI, + "Activation: (wifi) psk mismatch reported by supplicant, retrying connection"); + return; + } + _LOGI(LOGD_DEVICE | LOGD_WIFI, "Activation: (wifi) psk mismatch reported by supplicant, asking for new key"); @@ -2879,6 +2947,34 @@ supplicant_iface_notify_wpa_psk_mismatch_cb(NMSupplicantInterface *iface, NMDevi | NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW); } +static void +supplicant_iface_notify_wpa_sae_mismatch_cb(NMSupplicantInterface *iface, NMDeviceWifi *self) +{ + NMDevice *device = NM_DEVICE(self); + NMActRequest *req; + const char *setting_name = NM_SETTING_WIRELESS_SECURITY_SETTING_NAME; + + if (nm_device_get_state(device) != NM_DEVICE_STATE_CONFIG) + return; + + _LOGI(LOGD_DEVICE | LOGD_WIFI, + "Activation: (wifi) SAE password mismatch reported by supplicant, asking for new key"); + + req = nm_device_get_act_request(NM_DEVICE(self)); + g_return_if_fail(req != NULL); + + nm_act_request_clear_secrets(req); + + cleanup_association_attempt(self, TRUE); + nm_device_state_changed(device, + NM_DEVICE_STATE_NEED_AUTH, + NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT); + wifi_secrets_get_secrets(self, + setting_name, + NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION + | NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW); +} + /* * supplicant_connection_timeout_cb * @@ -3222,8 +3318,19 @@ act_stage1_prepare(NMDevice *device, NMDeviceStateReason *out_failure_reason) static void ensure_hotspot_frequency(NMDeviceWifi *self, NMSettingWireless *s_wifi, NMWifiAP *ap) { - guint32 a_freqs[] = {5180, 5200, 5220, 5745, 5765, 5785, 5805, 0}; - guint32 bg_freqs[] = {2412, 2437, 2462, 2472, 0}; + guint32 freqs_a[] = {5180, /* only U-NII-1 channels: non-DFS and available everywhere */ + 5200, + 5220, + 5240, + 0}; + guint32 freqs_bg[] = {2412, 2437, 2462, 2472, 0}; + guint32 freqs_6ghz[] = {5975, /* only U-NII-5 PSC channels, for better compatibility */ + 6055, + 6135, + 6215, + 6295, + 6375, + 0}; guint32 *rnd_freqs; guint rnd_freqs_len; NMDevice *device = NM_DEVICE(self); @@ -3234,7 +3341,7 @@ ensure_hotspot_frequency(NMDeviceWifi *self, NMSettingWireless *s_wifi, NMWifiAP guint l; nm_assert(ap); - nm_assert(NM_IN_STRSET(band, NULL, "a", "bg")); + nm_assert(NM_IN_STRSET(band, NULL, "a", "bg", "6GHz")); if (nm_wifi_ap_get_freq(ap)) return; @@ -3268,11 +3375,14 @@ ensure_hotspot_frequency(NMDeviceWifi *self, NMSettingWireless *s_wifi, NMWifiAP } if (nm_streq0(band, "a")) { - rnd_freqs = a_freqs; - rnd_freqs_len = G_N_ELEMENTS(a_freqs) - 1; + rnd_freqs = freqs_a; + rnd_freqs_len = G_N_ELEMENTS(freqs_a) - 1; + } else if (nm_streq0(band, "6GHz")) { + rnd_freqs = freqs_6ghz; + rnd_freqs_len = G_N_ELEMENTS(freqs_6ghz) - 1; } else { - rnd_freqs = bg_freqs; - rnd_freqs_len = G_N_ELEMENTS(bg_freqs) - 1; + rnd_freqs = freqs_bg; + rnd_freqs_len = G_N_ELEMENTS(freqs_bg) - 1; } /* shuffle the frequencies (inplace). The idea is to choose diff --git a/src/core/devices/wifi/nm-wifi-ap.c b/src/core/devices/wifi/nm-wifi-ap.c index ceb954b7..3e758972 100644 --- a/src/core/devices/wifi/nm-wifi-ap.c +++ b/src/core/devices/wifi/nm-wifi-ap.c @@ -574,16 +574,6 @@ nm_wifi_ap_to_string(const NMWifiAP *self, char *str_buf, gulong buf_len, gint64 return str_buf; } -static guint -freq_to_band(guint32 freq) -{ - if (freq >= 4915 && freq <= 5825) - return 5; - else if (freq >= 2412 && freq <= 2484) - return 2; - return 0; -} - gboolean nm_wifi_ap_check_compatible(NMWifiAP *self, NMConnection *connection) { @@ -631,12 +621,12 @@ nm_wifi_ap_check_compatible(NMWifiAP *self, NMConnection *connection) band = nm_setting_wireless_get_band(s_wireless); if (band) { - guint ap_band = freq_to_band(priv->freq); + const char *ap_band = nm_wifi_freq_to_band_prop(priv->freq); - if (!strcmp(band, "a") && ap_band != 5) - return FALSE; - else if (!strcmp(band, "bg") && ap_band != 2) + if (!nm_streq(band, ap_band)) return FALSE; + + return TRUE; } channel = nm_setting_wireless_get_channel(s_wireless); diff --git a/src/core/devices/wifi/nm-wifi-utils.c b/src/core/devices/wifi/nm-wifi-utils.c index 332352ab..2ee4ec2e 100644 --- a/src/core/devices/wifi/nm-wifi-utils.c +++ b/src/core/devices/wifi/nm-wifi-utils.c @@ -639,7 +639,7 @@ nm_wifi_utils_complete_connection(GBytes *ap_ssid, chan_valid = FALSE; } - band = nm_utils_wifi_freq_to_band(ap_freq); + band = nm_wifi_freq_to_band_prop(ap_freq); if (band) { g_object_set(s_wifi, NM_SETTING_WIRELESS_BAND, band, NULL); } else { @@ -890,6 +890,28 @@ nm_wifi_utils_is_manf_default_ssid(GBytes *ssid) return FALSE; } +/* Convert a WPS "Key" credential into a PSK string. The key is either an + * 8..63 character passphrase or a 64 character hexadecimal PSK. The actual + * WPA-PSK validity check is shared with nm_utils_wpa_psk_valid(). */ +gboolean +nm_wifi_utils_wps_key_to_psk(const guint8 *key, gsize key_len, char (*out_psk)[65]) +{ + if (key_len > 64) + return FALSE; + if (key_len < 64 && !g_utf8_validate((const char *) key, key_len, NULL)) + return FALSE; + + memcpy(*out_psk, key, key_len); + (*out_psk)[key_len] = '\0'; + + /* An embedded NUL would make nm_utils_wpa_psk_valid() see a truncated + * string, so reject it explicitly. */ + if (strlen(*out_psk) != key_len) + return FALSE; + + return nm_utils_wpa_psk_valid(*out_psk); +} + /* To be used for connections where the SSID has been validated before */ gboolean nm_wifi_connection_get_iwd_ssid_and_security(NMConnection *connection, @@ -1929,3 +1951,19 @@ nm_wifi_utils_wfd_info_eq(const NMIwdWfdInfo *a, const NMIwdWfdInfo *b) return a->source == b->source && a->sink == b->sink && a->port == b->port && a->has_audio == b->has_audio && a->has_uibc == b->has_uibc && a->has_cp == b->has_cp; } + +const char * +nm_wifi_freq_to_band_prop(guint32 freq) +{ + switch (nm_utils_wifi_freq_to_band(freq)) { + case NM_WIFI_BAND_2_4_GHZ: + return "bg"; + case NM_WIFI_BAND_5_GHZ: + return "a"; + case NM_WIFI_BAND_6_GHZ: + return "6GHz"; + default: + case NM_WIFI_BAND_UNKNOWN: + return NULL; + } +} diff --git a/src/core/devices/wifi/nm-wifi-utils.h b/src/core/devices/wifi/nm-wifi-utils.h index 1d46a900..30920396 100644 --- a/src/core/devices/wifi/nm-wifi-utils.h +++ b/src/core/devices/wifi/nm-wifi-utils.h @@ -42,6 +42,8 @@ gboolean nm_wifi_utils_complete_connection(GBytes *ssid, gboolean nm_wifi_utils_is_manf_default_ssid(GBytes *ssid); +gboolean nm_wifi_utils_wps_key_to_psk(const guint8 *key, gsize key_len, char (*out_psk)[65]); + gboolean nm_wifi_connection_get_iwd_ssid_and_security(NMConnection *connection, char **ssid, NMIwdNetworkSecurity *security); @@ -56,4 +58,6 @@ bool nm_wifi_utils_parse_wfd_ies(GBytes *ies, NMIwdWfdInfo *out_wfd); GBytes *nm_wifi_utils_build_wfd_ies(const NMIwdWfdInfo *wfd); bool nm_wifi_utils_wfd_info_eq(const NMIwdWfdInfo *a, const NMIwdWfdInfo *b); +const char *nm_wifi_freq_to_band_prop(guint32 freq); + #endif /* __NM_WIFI_UTILS_H__ */ diff --git a/src/core/devices/wifi/tests/test-devices-wifi.c b/src/core/devices/wifi/tests/test-devices-wifi.c index a52696ea..b50e2626 100644 --- a/src/core/devices/wifi/tests/test-devices-wifi.c +++ b/src/core/devices/wifi/tests/test-devices-wifi.c @@ -1459,6 +1459,50 @@ test_ssids_options_to_ptrarray(void) /*****************************************************************************/ +static void +test_wps_key_to_psk(void) +{ + static const struct { + const char *key; + gsize key_len; + gboolean valid; + } cases[] = { + {"12345678", 8, TRUE}, + {"supersecret123", 14, TRUE}, + {"caf\xc3\xa9_key", 9, TRUE}, + {"123456789012345678901234567890123456789012345678901234567890123", 63, TRUE}, + {"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", 64, TRUE}, + {"0123456789ABCDEF0123456789abcdef0123456789abcdef0123456789abcdeF", 64, TRUE}, + {"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdeg", 64, FALSE}, + {"", 0, FALSE}, + {"1234567", 7, FALSE}, + {"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0", 65, FALSE}, + {"abc\xff" + "def123", + 10, + FALSE}, + {"0123456789abcdef0123456789abcdef\0" + "23456789abcdef0123456789abcdef0", + 64, + FALSE}, + }; + gsize i; + + for (i = 0; i < G_N_ELEMENTS(cases); i++) { + char psk[65]; + gboolean ok; + + ok = nm_wifi_utils_wps_key_to_psk((const guint8 *) cases[i].key, cases[i].key_len, &psk); + g_assert_cmpint(ok, ==, cases[i].valid); + if (ok) { + g_assert_cmpmem(psk, cases[i].key_len, cases[i].key, cases[i].key_len); + g_assert_cmpint(psk[cases[i].key_len], ==, '\0'); + } + } +} + +/*****************************************************************************/ + NMTST_DEFINE(); int @@ -1605,5 +1649,7 @@ main(int argc, char **argv) g_test_add_func("/wifi/ssids_options_to_ptrarray", test_ssids_options_to_ptrarray); + g_test_add_func("/wifi/wps_key_to_psk", test_wps_key_to_psk); + return g_test_run(); } |