diff options
Diffstat (limited to 'src/core/devices/wifi/nm-wifi-utils.c')
-rw-r--r-- | src/core/devices/wifi/nm-wifi-utils.c | 943 |
1 files changed, 943 insertions, 0 deletions
diff --git a/src/core/devices/wifi/nm-wifi-utils.c b/src/core/devices/wifi/nm-wifi-utils.c new file mode 100644 index 0000000000..f0b772c647 --- /dev/null +++ b/src/core/devices/wifi/nm-wifi-utils.c @@ -0,0 +1,943 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2011 Red Hat, Inc. + */ + +#include "nm-default.h" + +#include "nm-wifi-utils.h" + +#include <stdlib.h> + +#include "nm-utils.h" +#include "nm-core-internal.h" + +static gboolean +verify_no_wep(NMSettingWirelessSecurity *s_wsec, const char *tag, GError **error) +{ + if (nm_setting_wireless_security_get_wep_key(s_wsec, 0) + || nm_setting_wireless_security_get_wep_key(s_wsec, 1) + || nm_setting_wireless_security_get_wep_key(s_wsec, 2) + || nm_setting_wireless_security_get_wep_key(s_wsec, 3) + || nm_setting_wireless_security_get_wep_tx_keyidx(s_wsec) + || nm_setting_wireless_security_get_wep_key_type(s_wsec)) { + /* Dynamic WEP cannot have any WEP keys set */ + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_SETTING, + _("%s is incompatible with static WEP keys"), + tag); + g_prefix_error(error, "%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME); + return FALSE; + } + + return TRUE; +} + +static gboolean +verify_leap(NMSettingWirelessSecurity *s_wsec, + NMSetting8021x * s_8021x, + gboolean adhoc, + GError ** error) +{ + const char *key_mgmt, *auth_alg, *leap_username; + + key_mgmt = nm_setting_wireless_security_get_key_mgmt(s_wsec); + auth_alg = nm_setting_wireless_security_get_auth_alg(s_wsec); + leap_username = nm_setting_wireless_security_get_leap_username(s_wsec); + + /* One (or both) of two things indicates we want LEAP: + * 1) auth_alg == 'leap' + * 2) valid leap_username + * + * LEAP always requires a LEAP username. + */ + + if (auth_alg) { + if (!strcmp(auth_alg, "leap")) { + /* LEAP authentication requires at least a LEAP username */ + if (!leap_username) { + g_set_error_literal(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_MISSING_PROPERTY, + _("LEAP authentication requires a LEAP username")); + g_prefix_error(error, + "%s.%s: ", + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME); + return FALSE; + } + } else if (leap_username) { + /* Leap username requires 'leap' auth */ + g_set_error_literal(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("LEAP username requires 'leap' authentication")); + g_prefix_error(error, + "%s.%s: ", + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME); + return FALSE; + } + } + + if (leap_username) { + if (key_mgmt && strcmp(key_mgmt, "ieee8021x")) { + /* LEAP requires ieee8021x key management */ + g_set_error_literal(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("LEAP authentication requires IEEE 802.1x key management")); + g_prefix_error(error, + "%s.%s: ", + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT); + return FALSE; + } + } + + /* At this point if auth_alg is set it must be 'leap', and if key_mgmt + * is set it must be 'ieee8021x'. + */ + if (leap_username) { + if (auth_alg) + g_assert(strcmp(auth_alg, "leap") == 0); + if (key_mgmt) + g_assert(strcmp(key_mgmt, "ieee8021x") == 0); + + if (adhoc) { + g_set_error_literal(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_SETTING, + _("LEAP authentication is incompatible with Ad-Hoc mode")); + g_prefix_error(error, "%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME); + return FALSE; + } + + if (!verify_no_wep(s_wsec, "LEAP", error)) + return FALSE; + + if (s_8021x) { + g_set_error_literal(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_SETTING, + _("LEAP authentication is incompatible with 802.1x setting")); + g_prefix_error(error, "%s: ", NM_SETTING_802_1X_SETTING_NAME); + return FALSE; + } + } + + return TRUE; +} + +static gboolean +verify_no_wpa(NMSettingWirelessSecurity *s_wsec, const char *tag, GError **error) +{ + const char *key_mgmt; + int n, i; + + key_mgmt = nm_setting_wireless_security_get_key_mgmt(s_wsec); + if (key_mgmt && !strncmp(key_mgmt, "wpa", 3)) { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("a connection using '%s' authentication cannot use WPA key management"), + tag); + g_prefix_error(error, + "%s.%s: ", + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT); + return FALSE; + } + + if (nm_setting_wireless_security_get_num_protos(s_wsec)) { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("a connection using '%s' authentication cannot specify WPA protocols"), + tag); + g_prefix_error(error, + "%s.%s: ", + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + NM_SETTING_WIRELESS_SECURITY_PROTO); + return FALSE; + } + + n = nm_setting_wireless_security_get_num_pairwise(s_wsec); + for (i = 0; i < n; i++) { + const char *pw; + + pw = nm_setting_wireless_security_get_pairwise(s_wsec, i); + if (!strcmp(pw, "tkip") || !strcmp(pw, "ccmp")) { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("a connection using '%s' authentication cannot specify WPA ciphers"), + tag); + g_prefix_error(error, + "%s.%s: ", + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + NM_SETTING_WIRELESS_SECURITY_PAIRWISE); + return FALSE; + } + } + + n = nm_setting_wireless_security_get_num_groups(s_wsec); + for (i = 0; i < n; i++) { + const char *gr; + + gr = nm_setting_wireless_security_get_group(s_wsec, i); + if (strcmp(gr, "wep40") && strcmp(gr, "wep104")) { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("a connection using '%s' authentication cannot specify WPA ciphers"), + tag); + g_prefix_error(error, + "%s.%s: ", + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + NM_SETTING_WIRELESS_SECURITY_GROUP); + return FALSE; + } + } + + if (nm_setting_wireless_security_get_psk(s_wsec)) { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("a connection using '%s' authentication cannot specify a WPA password"), + tag); + g_prefix_error(error, + "%s.%s: ", + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + NM_SETTING_WIRELESS_SECURITY_PSK); + return FALSE; + } + + return TRUE; +} + +static gboolean +verify_dynamic_wep(NMSettingWirelessSecurity *s_wsec, + NMSetting8021x * s_8021x, + gboolean adhoc, + GError ** error) +{ + const char *key_mgmt, *auth_alg, *leap_username; + + key_mgmt = nm_setting_wireless_security_get_key_mgmt(s_wsec); + auth_alg = nm_setting_wireless_security_get_auth_alg(s_wsec); + leap_username = nm_setting_wireless_security_get_leap_username(s_wsec); + + g_return_val_if_fail(leap_username == NULL, TRUE); + + if (key_mgmt) { + if (!strcmp(key_mgmt, "ieee8021x")) { + if (!s_8021x) { + /* 802.1x key management requires an 802.1x setting */ + g_set_error_literal(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_MISSING_SETTING, + _("Dynamic WEP requires an 802.1x setting")); + g_prefix_error(error, "%s: ", NM_SETTING_802_1X_SETTING_NAME); + return FALSE; + } + + if (auth_alg && strcmp(auth_alg, "open")) { + /* 802.1x key management must use "open" authentication */ + g_set_error_literal(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("Dynamic WEP requires 'open' authentication")); + g_prefix_error(error, + "%s.%s: ", + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + NM_SETTING_WIRELESS_SECURITY_AUTH_ALG); + return FALSE; + } + + /* Dynamic WEP incompatible with anything static WEP related */ + if (!verify_no_wep(s_wsec, "Dynamic WEP", error)) + return FALSE; + } else if (!strcmp(key_mgmt, "none")) { + if (s_8021x) { + /* 802.1x setting requires 802.1x key management */ + g_set_error_literal(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("Dynamic WEP requires 'ieee8021x' key management")); + g_prefix_error(error, + "%s.%s: ", + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT); + return FALSE; + } + } + } else if (s_8021x) { + /* 802.1x setting incompatible with anything but 'open' auth */ + if (auth_alg && strcmp(auth_alg, "open")) { + /* 802.1x key management must use "open" authentication */ + g_set_error_literal(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("Dynamic WEP requires 'open' authentication")); + g_prefix_error(error, + "%s.%s: ", + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + NM_SETTING_WIRELESS_SECURITY_AUTH_ALG); + return FALSE; + } + + /* Dynamic WEP incompatible with anything static WEP related */ + if (!verify_no_wep(s_wsec, "Dynamic WEP", error)) + return FALSE; + } + + return TRUE; +} + +static gboolean +verify_wpa_psk(NMSettingWirelessSecurity *s_wsec, + NMSetting8021x * s_8021x, + gboolean adhoc, + guint32 wpa_flags, + guint32 rsn_flags, + GError ** error) +{ + const char *key_mgmt, *auth_alg; + + key_mgmt = nm_setting_wireless_security_get_key_mgmt(s_wsec); + auth_alg = nm_setting_wireless_security_get_auth_alg(s_wsec); + + if (!nm_streq0(key_mgmt, "wpa-psk")) + return TRUE; + + if (s_8021x) { + g_set_error_literal(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_SETTING, + _("WPA-PSK authentication is incompatible with 802.1x")); + g_prefix_error(error, "%s: ", NM_SETTING_802_1X_SETTING_NAME); + return FALSE; + } + + if (auth_alg && !nm_streq(auth_alg, "open")) { + /* WPA must use "open" authentication */ + g_set_error_literal(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("WPA-PSK requires 'open' authentication")); + g_prefix_error(error, + "%s.%s: ", + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + NM_SETTING_WIRELESS_SECURITY_AUTH_ALG); + return FALSE; + } + + /* Make sure the AP's capabilities support WPA-PSK */ + if (!(wpa_flags & NM_802_11_AP_SEC_KEY_MGMT_PSK) + && !(rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_PSK)) { + g_set_error_literal(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("Access point does not support PSK but setting requires it")); + g_prefix_error(error, + "%s.%s: ", + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT); + return FALSE; + } + + if (adhoc) { + /* Ad-Hoc RSN requires 'rsn' proto, 'ccmp' pairwise, and 'ccmp' group */ + if (nm_setting_wireless_security_get_num_protos(s_wsec) != 1 + || !nm_streq0(nm_setting_wireless_security_get_proto(s_wsec, 0), "rsn")) { + g_set_error_literal(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("WPA Ad-Hoc authentication requires 'rsn' protocol")); + g_prefix_error(error, + "%s.%s: ", + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + NM_SETTING_WIRELESS_SECURITY_PROTO); + return FALSE; + } + + if (nm_setting_wireless_security_get_num_pairwise(s_wsec) != 1 + || !nm_streq0(nm_setting_wireless_security_get_pairwise(s_wsec, 0), "ccmp")) { + g_set_error_literal(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("WPA Ad-Hoc authentication requires 'ccmp' pairwise cipher")); + g_prefix_error(error, + "%s.%s: ", + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + NM_SETTING_WIRELESS_SECURITY_PAIRWISE); + return FALSE; + } + + if (nm_setting_wireless_security_get_num_groups(s_wsec) != 1 + || !nm_streq0(nm_setting_wireless_security_get_group(s_wsec, 0), "ccmp")) { + g_set_error_literal(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("WPA Ad-Hoc requires 'ccmp' group cipher")); + g_prefix_error(error, + "%s.%s: ", + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + NM_SETTING_WIRELESS_SECURITY_GROUP); + return FALSE; + } + } + + return TRUE; +} + +static gboolean +verify_wpa_eap(NMSettingWirelessSecurity *s_wsec, + NMSetting8021x * s_8021x, + guint32 wpa_flags, + guint32 rsn_flags, + GError ** error) +{ + const char *key_mgmt, *auth_alg; + gboolean is_wpa_eap = FALSE; + + key_mgmt = nm_setting_wireless_security_get_key_mgmt(s_wsec); + auth_alg = nm_setting_wireless_security_get_auth_alg(s_wsec); + + if (key_mgmt) { + if (NM_IN_STRSET(key_mgmt, "wpa-eap", "wpa-eap-suite-b-192")) { + if (!s_8021x) { + g_set_error_literal(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_MISSING_SETTING, + _("WPA-EAP authentication requires an 802.1x setting")); + g_prefix_error(error, "%s: ", NM_SETTING_802_1X_SETTING_NAME); + return FALSE; + } + + if (auth_alg && strcmp(auth_alg, "open")) { + /* WPA must use "open" authentication */ + g_set_error_literal(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("WPA-EAP requires 'open' authentication")); + g_prefix_error(error, + "%s.%s: ", + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + NM_SETTING_WIRELESS_SECURITY_AUTH_ALG); + return FALSE; + } + + is_wpa_eap = TRUE; + } else if (s_8021x) { + g_set_error_literal(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_SETTING, + _("802.1x setting requires 'wpa-eap' key management")); + g_prefix_error(error, "%s: ", NM_SETTING_802_1X_SETTING_NAME); + return FALSE; + } + } + + if (is_wpa_eap || s_8021x) { + /* Make sure the AP's capabilities support WPA-EAP */ + if (!(wpa_flags & NM_802_11_AP_SEC_KEY_MGMT_802_1X) + && !(rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_802_1X) + && !(rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_EAP_SUITE_B_192)) { + g_set_error_literal(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_SETTING, + _("Access point does not support 802.1x but setting requires it")); + g_prefix_error(error, "%s: ", NM_SETTING_802_1X_SETTING_NAME); + return FALSE; + } + } + + return TRUE; +} + +static gboolean +verify_adhoc(NMSettingWirelessSecurity *s_wsec, + NMSetting8021x * s_8021x, + gboolean adhoc, + GError ** error) +{ + const char *key_mgmt = NULL, *leap_username = NULL, *auth_alg = NULL; + + if (!adhoc) + return TRUE; + + if (s_wsec) { + key_mgmt = nm_setting_wireless_security_get_key_mgmt(s_wsec); + auth_alg = nm_setting_wireless_security_get_auth_alg(s_wsec); + leap_username = nm_setting_wireless_security_get_leap_username(s_wsec); + } + + if (key_mgmt && !NM_IN_STRSET(key_mgmt, "none", "wpa-psk")) { + g_set_error_literal(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("Ad-Hoc mode requires 'none' or 'wpa-psk' key management")); + g_prefix_error(error, + "%s.%s: ", + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT); + return FALSE; + } + + if (s_8021x) { + g_set_error_literal(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_SETTING, + _("Ad-Hoc mode is incompatible with 802.1x security")); + g_prefix_error(error, "%s: ", NM_SETTING_802_1X_SETTING_NAME); + return FALSE; + } + + if (leap_username) { + g_set_error_literal(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("Ad-Hoc mode is incompatible with LEAP security")); + g_prefix_error(error, + "%s.%s: ", + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + NM_SETTING_WIRELESS_SECURITY_AUTH_ALG); + return FALSE; + } + + if (auth_alg && !nm_streq(auth_alg, "open")) { + g_set_error_literal(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("Ad-Hoc mode requires 'open' authentication")); + g_prefix_error(error, + "%s.%s: ", + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + NM_SETTING_WIRELESS_SECURITY_AUTH_ALG); + return FALSE; + } + + return TRUE; +} + +gboolean +nm_wifi_utils_complete_connection(GBytes * ap_ssid, + const char * bssid, + NM80211Mode ap_mode, + guint32 ap_freq, + guint32 ap_flags, + guint32 ap_wpa_flags, + guint32 ap_rsn_flags, + NMConnection *connection, + gboolean lock_bssid, + GError ** error) +{ + NMSettingWireless * s_wifi; + NMSettingWirelessSecurity *s_wsec; + NMSetting8021x * s_8021x; + GBytes * ssid; + const char * mode, *key_mgmt, *auth_alg, *leap_username; + gboolean adhoc = FALSE; + gboolean mesh = FALSE; + + s_wifi = nm_connection_get_setting_wireless(connection); + g_assert(s_wifi); + s_wsec = nm_connection_get_setting_wireless_security(connection); + s_8021x = nm_connection_get_setting_802_1x(connection); + + /* Fill in missing SSID */ + ssid = nm_setting_wireless_get_ssid(s_wifi); + if (!ssid) + g_object_set(G_OBJECT(s_wifi), NM_SETTING_WIRELESS_SSID, ap_ssid, NULL); + else if (!ap_ssid || !g_bytes_equal(ssid, ap_ssid)) { + g_set_error_literal(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("connection does not match access point")); + g_prefix_error(error, + "%s.%s: ", + NM_SETTING_WIRELESS_SETTING_NAME, + NM_SETTING_WIRELESS_SSID); + return FALSE; + } + + if (lock_bssid && !nm_setting_wireless_get_bssid(s_wifi)) + g_object_set(G_OBJECT(s_wifi), NM_SETTING_WIRELESS_BSSID, bssid, NULL); + + /* And mode */ + mode = nm_setting_wireless_get_mode(s_wifi); + if (mode) { + gboolean valid = FALSE; + + /* Make sure the supplied mode matches the AP's */ + if (!strcmp(mode, NM_SETTING_WIRELESS_MODE_INFRA) + || !strcmp(mode, NM_SETTING_WIRELESS_MODE_AP)) { + if (ap_mode == NM_802_11_MODE_INFRA) + valid = TRUE; + } else if (!strcmp(mode, NM_SETTING_WIRELESS_MODE_ADHOC)) { + if (ap_mode == NM_802_11_MODE_ADHOC) + valid = TRUE; + adhoc = TRUE; + } else if (!strcmp(mode, NM_SETTING_WIRELESS_MODE_MESH)) { + if (ap_mode == NM_802_11_MODE_MESH) + valid = TRUE; + mesh = TRUE; + } + + if (valid == FALSE) { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("connection does not match access point")); + g_prefix_error(error, + "%s.%s: ", + NM_SETTING_WIRELESS_SETTING_NAME, + NM_SETTING_WIRELESS_MODE); + return FALSE; + } + } else { + mode = NM_SETTING_WIRELESS_MODE_INFRA; + if (ap_mode == NM_802_11_MODE_ADHOC) { + mode = NM_SETTING_WIRELESS_MODE_ADHOC; + adhoc = TRUE; + } else if (ap_mode == NM_802_11_MODE_MESH) { + mode = NM_SETTING_WIRELESS_MODE_MESH; + mesh = TRUE; + } + g_object_set(G_OBJECT(s_wifi), NM_SETTING_WIRELESS_MODE, mode, NULL); + } + + /* For now mesh requires channel and band, fill them only if both not present. + * Do not check existing values against an existing ap/mesh point, + * mesh join will start a new network if required */ + if (mesh) { + const char *band; + guint32 channel; + gboolean band_valid = TRUE; + gboolean chan_valid = TRUE; + gboolean valid; + + band = nm_setting_wireless_get_band(s_wifi); + channel = nm_setting_wireless_get_channel(s_wifi); + + valid = ((band == NULL) && (channel == 0)) || ((band != NULL) && (channel != 0)); + + if ((band == NULL) && (channel == 0)) { + channel = nm_utils_wifi_freq_to_channel(ap_freq); + if (channel) { + g_object_set(s_wifi, NM_SETTING_WIRELESS_CHANNEL, channel, NULL); + } else { + chan_valid = FALSE; + } + + band = nm_utils_wifi_freq_to_band(ap_freq); + if (band) { + g_object_set(s_wifi, NM_SETTING_WIRELESS_BAND, band, NULL); + } else { + band_valid = FALSE; + } + } + + if (!valid || !chan_valid || !band_valid) { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("connection does not match mesh point")); + g_prefix_error(error, + "%s.%s: ", + NM_SETTING_WIRELESS_SETTING_NAME, + NM_SETTING_WIRELESS_MODE); + return FALSE; + } + } + + /* Security */ + + /* Open */ + if (!(ap_flags & NM_802_11_AP_FLAGS_PRIVACY) && (ap_wpa_flags == NM_802_11_AP_SEC_NONE) + && (ap_rsn_flags == NM_802_11_AP_SEC_NONE)) { + /* Make sure the connection doesn't specify security */ + if (s_wsec || s_8021x) { + g_set_error_literal(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_SETTING, + _("Access point is unencrypted but setting specifies security")); + if (s_wsec) + g_prefix_error(error, "%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME); + else + g_prefix_error(error, "%s: ", NM_SETTING_802_1X_SETTING_NAME); + return FALSE; + } + return TRUE; + } + + /* Everything else requires security */ + if (!s_wsec) { + s_wsec = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new(); + nm_connection_add_setting(connection, NM_SETTING(s_wsec)); + } + + key_mgmt = nm_setting_wireless_security_get_key_mgmt(s_wsec); + auth_alg = nm_setting_wireless_security_get_auth_alg(s_wsec); + leap_username = nm_setting_wireless_security_get_leap_username(s_wsec); + + /* Ad-Hoc checks */ + if (!verify_adhoc(s_wsec, s_8021x, adhoc, error)) + return FALSE; + + /* Static WEP, Dynamic WEP, or LEAP */ + if ((ap_flags & NM_802_11_AP_FLAGS_PRIVACY) && (ap_wpa_flags == NM_802_11_AP_SEC_NONE) + && (ap_rsn_flags == NM_802_11_AP_SEC_NONE)) { + const char *tag = "WEP"; + gboolean is_dynamic_wep = FALSE; + + if (!verify_leap(s_wsec, s_8021x, adhoc, error)) + return FALSE; + + if (leap_username) { + tag = "LEAP"; + } else { + /* Static or Dynamic WEP */ + if (!verify_dynamic_wep(s_wsec, s_8021x, adhoc, error)) + return FALSE; + + if (s_8021x || (key_mgmt && !strcmp(key_mgmt, "ieee8021x"))) { + is_dynamic_wep = TRUE; + tag = "Dynamic WEP"; + } + } + + /* Nothing WPA-related can be set */ + if (!verify_no_wpa(s_wsec, tag, error)) + return FALSE; + + if (leap_username) { + /* LEAP */ + g_object_set(s_wsec, + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, + "ieee8021x", + NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, + "leap", + NULL); + } else if (is_dynamic_wep) { + /* Dynamic WEP */ + g_object_set(s_wsec, + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, + "ieee8021x", + NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, + "open", + NULL); + + if (s_8021x) { + /* Dynamic WEP requires a valid 802.1x setting since we can't + * autocomplete 802.1x. + */ + if (!nm_setting_verify(NM_SETTING(s_8021x), NULL, error)) + return FALSE; + } + } else { + /* Static WEP */ + g_object_set(s_wsec, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "none", NULL); + } + + return TRUE; + } + + /* WPA/RSN */ + g_assert(ap_wpa_flags || ap_rsn_flags); + + /* Ensure key management is valid for WPA */ + if ((key_mgmt && !strcmp(key_mgmt, "ieee8021x")) || leap_username) { + g_set_error_literal( + error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("WPA authentication is incompatible with non-EAP (original) LEAP or Dynamic WEP")); + g_prefix_error(error, + "%s.%s: ", + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT); + return FALSE; + } + + /* 'shared' auth incompatible with any type of WPA */ + if (auth_alg && strcmp(auth_alg, "open")) { + g_set_error_literal(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("WPA authentication is incompatible with Shared Key authentication")); + g_prefix_error(error, + "%s.%s: ", + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + NM_SETTING_WIRELESS_SECURITY_AUTH_ALG); + return FALSE; + } + + if (!verify_no_wep(s_wsec, "WPA", error)) + return FALSE; + + if (!verify_wpa_psk(s_wsec, s_8021x, adhoc, ap_wpa_flags, ap_rsn_flags, error)) + return FALSE; + + if (!adhoc && !verify_wpa_eap(s_wsec, s_8021x, ap_wpa_flags, ap_rsn_flags, error)) + return FALSE; + + if (adhoc) { + g_object_set(s_wsec, + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, + "wpa-psk", + NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, + "open", + NULL); + nm_setting_wireless_security_add_proto(s_wsec, "rsn"); + nm_setting_wireless_security_add_pairwise(s_wsec, "ccmp"); + nm_setting_wireless_security_add_group(s_wsec, "ccmp"); + } else if (s_8021x) { + g_object_set(s_wsec, + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, + "wpa-eap", + NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, + "open", + NULL); + /* Leave proto/pairwise/group as client set them; if they are unset the + * supplicant will figure out the best combination at connect time. + */ + + /* 802.1x also requires the client to completely fill in the 8021x + * setting. Since there's so much configuration required for it, there's + * no way it can be automatically completed. + */ + } else if ((key_mgmt && !strcmp(key_mgmt, "sae")) + || (ap_rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_SAE)) { + g_object_set(s_wsec, + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, + "sae", + NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, + "open", + NULL); + } else if ((key_mgmt && !strcmp(key_mgmt, "owe")) + || NM_FLAGS_ANY(ap_rsn_flags, + NM_802_11_AP_SEC_KEY_MGMT_OWE | NM_802_11_AP_SEC_KEY_MGMT_OWE_TM)) { + g_object_set(s_wsec, + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, + "owe", + NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, + "open", + NULL); + } else if ((key_mgmt && !strcmp(key_mgmt, "wpa-psk")) + || (ap_wpa_flags & NM_802_11_AP_SEC_KEY_MGMT_PSK) + || (ap_rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_PSK)) { + g_object_set(s_wsec, + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, + "wpa-psk", + NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, + "open", + NULL); + /* Leave proto/pairwise/group as client set them; if they are unset the + * supplicant will figure out the best combination at connect time. + */ + } else if ((key_mgmt && !strcmp(key_mgmt, "wpa-eap-suite-b-192")) + || (ap_rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_EAP_SUITE_B_192)) { + g_object_set(s_wsec, + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, + "wpa-eap-suite-b-192", + NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, + "open", + NULL); + } else { + g_set_error_literal(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_FAILED, + _("Failed to determine AP security information")); + return FALSE; + } + + return TRUE; +} + +gboolean +nm_wifi_utils_is_manf_default_ssid(GBytes *ssid) +{ + const guint8 *ssid_p; + gsize ssid_l; + int i; + /* + * List of manufacturer default SSIDs that are often unchanged by users. + * + * NOTE: this list should *not* contain networks that you would like to + * automatically roam to like "Starbucks" or "AT&T" or "T-Mobile HotSpot". + */ + static const char *manf_defaults[] = { + "linksys", + "linksys-a", + "linksys-g", + "default", + "belkin54g", + "NETGEAR", + "o2DSL", + "WLAN", + "ALICE-WLAN", + "Speedport W 501V", + "TURBONETT", + }; + + ssid_p = g_bytes_get_data(ssid, &ssid_l); + + for (i = 0; i < G_N_ELEMENTS(manf_defaults); i++) { + if (ssid_l == strlen(manf_defaults[i])) { + if (memcmp(manf_defaults[i], ssid_p, ssid_l) == 0) + return TRUE; + } + } + return FALSE; +} + +/* To be used for connections where the SSID has been validated before */ +gboolean +nm_wifi_connection_get_iwd_ssid_and_security(NMConnection * connection, + char ** ssid, + NMIwdNetworkSecurity *security) +{ + NMSettingWireless * s_wireless; + NMSettingWirelessSecurity *s_wireless_sec; + const char * key_mgmt = NULL; + + s_wireless = nm_connection_get_setting_wireless(connection); + if (!s_wireless) + return FALSE; + + if (ssid) { + GBytes * bytes = nm_setting_wireless_get_ssid(s_wireless); + gsize ssid_len; + const char *ssid_str = (const char *) g_bytes_get_data(bytes, &ssid_len); + + nm_assert(bytes && g_utf8_validate(ssid_str, ssid_len, NULL)); + NM_SET_OUT(ssid, g_strndup(ssid_str, ssid_len)); + } + + if (!security) + return TRUE; + + s_wireless_sec = nm_connection_get_setting_wireless_security(connection); + if (!s_wireless_sec) { + NM_SET_OUT(security, NM_IWD_NETWORK_SECURITY_NONE); + return TRUE; + } + + key_mgmt = nm_setting_wireless_security_get_key_mgmt(s_wireless_sec); + nm_assert(key_mgmt); + + if (NM_IN_STRSET(key_mgmt, "none", "ieee8021x")) + NM_SET_OUT(security, NM_IWD_NETWORK_SECURITY_WEP); + else if (nm_streq(key_mgmt, "wpa-psk")) + NM_SET_OUT(security, NM_IWD_NETWORK_SECURITY_PSK); + else if (nm_streq(key_mgmt, "wpa-eap")) + NM_SET_OUT(security, NM_IWD_NETWORK_SECURITY_8021X); + else + return FALSE; + + return TRUE; +} |