about summary refs log tree commit diff
path: root/src/core/devices/wifi/nm-wifi-utils.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/devices/wifi/nm-wifi-utils.c')
-rw-r--r--src/core/devices/wifi/nm-wifi-utils.c256
1 files changed, 202 insertions, 54 deletions
diff --git a/src/core/devices/wifi/nm-wifi-utils.c b/src/core/devices/wifi/nm-wifi-utils.c
index 4939e786..1a8204b0 100644
--- a/src/core/devices/wifi/nm-wifi-utils.c
+++ b/src/core/devices/wifi/nm-wifi-utils.c
@@ -41,9 +41,9 @@ verify_no_wep(NMSettingWirelessSecurity *s_wsec, const char *tag, GError **error
 
 static gboolean
 verify_leap(NMSettingWirelessSecurity *s_wsec,
-            NMSetting8021x *           s_8021x,
+            NMSetting8021x            *s_8021x,
             gboolean                   adhoc,
-            GError **                  error)
+            GError                   **error)
 {
     const char *key_mgmt, *auth_alg, *leap_username;
 
@@ -224,9 +224,9 @@ verify_no_wpa(NMSettingWirelessSecurity *s_wsec, const char *tag, GError **error
 
 static gboolean
 verify_dynamic_wep(NMSettingWirelessSecurity *s_wsec,
-                   NMSetting8021x *           s_8021x,
+                   NMSetting8021x            *s_8021x,
                    gboolean                   adhoc,
-                   GError **                  error)
+                   GError                   **error)
 {
     const char *key_mgmt, *auth_alg, *leap_username;
 
@@ -303,11 +303,11 @@ verify_dynamic_wep(NMSettingWirelessSecurity *s_wsec,
 
 static gboolean
 verify_wpa_psk(NMSettingWirelessSecurity *s_wsec,
-               NMSetting8021x *           s_8021x,
+               NMSetting8021x            *s_8021x,
                gboolean                   adhoc,
                guint32                    wpa_flags,
                guint32                    rsn_flags,
-               GError **                  error)
+               GError                   **error)
 {
     const char *key_mgmt, *auth_alg;
 
@@ -400,10 +400,10 @@ verify_wpa_psk(NMSettingWirelessSecurity *s_wsec,
 
 static gboolean
 verify_wpa_eap(NMSettingWirelessSecurity *s_wsec,
-               NMSetting8021x *           s_8021x,
+               NMSetting8021x            *s_8021x,
                guint32                    wpa_flags,
                guint32                    rsn_flags,
-               GError **                  error)
+               GError                   **error)
 {
     const char *key_mgmt, *auth_alg;
     gboolean    is_wpa_eap = FALSE;
@@ -465,9 +465,9 @@ verify_wpa_eap(NMSettingWirelessSecurity *s_wsec,
 
 static gboolean
 verify_adhoc(NMSettingWirelessSecurity *s_wsec,
-             NMSetting8021x *           s_8021x,
+             NMSetting8021x            *s_8021x,
              gboolean                   adhoc,
-             GError **                  error)
+             GError                   **error)
 {
     const char *key_mgmt = NULL, *leap_username = NULL, *auth_alg = NULL;
 
@@ -529,8 +529,8 @@ verify_adhoc(NMSettingWirelessSecurity *s_wsec,
 }
 
 gboolean
-nm_wifi_utils_complete_connection(GBytes *      ap_ssid,
-                                  const char *  bssid,
+nm_wifi_utils_complete_connection(GBytes       *ap_ssid,
+                                  const char   *bssid,
                                   _NM80211Mode  ap_mode,
                                   guint32       ap_freq,
                                   guint32       ap_flags,
@@ -538,13 +538,13 @@ nm_wifi_utils_complete_connection(GBytes *      ap_ssid,
                                   guint32       ap_rsn_flags,
                                   NMConnection *connection,
                                   gboolean      lock_bssid,
-                                  GError **     error)
+                                  GError      **error)
 {
-    NMSettingWireless *        s_wifi;
+    NMSettingWireless         *s_wifi;
     NMSettingWirelessSecurity *s_wsec;
-    NMSetting8021x *           s_8021x;
-    GBytes *                   ssid;
-    const char *               mode, *key_mgmt, *auth_alg, *leap_username;
+    NMSetting8021x            *s_8021x;
+    GBytes                    *ssid;
+    const char                *mode, *key_mgmt, *auth_alg, *leap_username;
     gboolean                   adhoc = FALSE;
     gboolean                   mesh  = FALSE;
 
@@ -892,20 +892,20 @@ nm_wifi_utils_is_manf_default_ssid(GBytes *ssid)
 
 /* 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,
+nm_wifi_connection_get_iwd_ssid_and_security(NMConnection         *connection,
+                                             char                **ssid,
                                              NMIwdNetworkSecurity *security)
 {
-    NMSettingWireless *        s_wireless;
+    NMSettingWireless         *s_wireless;
     NMSettingWirelessSecurity *s_wireless_sec;
-    const char *               key_mgmt = NULL;
+    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);
+        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);
 
@@ -947,7 +947,7 @@ nm_wifi_connection_get_iwd_ssid_and_security(NMConnection *        connection,
  * @ssid_len can be -1 instead of actual SSID length.
  */
 char *
-nm_wifi_utils_get_iwd_config_filename(const char *         ssid,
+nm_wifi_utils_get_iwd_config_filename(const char          *ssid,
                                       gssize               ssid_len,
                                       NMIwdNetworkSecurity security)
 {
@@ -995,10 +995,10 @@ static gboolean
 psk_setting_to_iwd_config(GKeyFile *file, NMSettingWirelessSecurity *s_wsec, GError **error)
 {
     NMSettingSecretFlags psk_flags = nm_setting_wireless_security_get_psk_flags(s_wsec);
-    const char *         psk       = nm_setting_wireless_security_get_psk(s_wsec);
+    const char          *psk       = nm_setting_wireless_security_get_psk(s_wsec);
     gsize                psk_len;
     guint8               buffer[32];
-    const char *         key_mgmt = nm_setting_wireless_security_get_key_mgmt(s_wsec);
+    const char          *key_mgmt = nm_setting_wireless_security_get_key_mgmt(s_wsec);
 
     if (!psk || NM_FLAGS_ANY(psk_flags, SECRETS_DONT_STORE_FLAGS)) {
         if (NM_FLAGS_ANY(psk_flags, SECRETS_DONT_STORE_FLAGS)) {
@@ -1029,11 +1029,11 @@ psk_setting_to_iwd_config(GKeyFile *file, NMSettingWirelessSecurity *s_wsec, GEr
 }
 
 static gboolean
-eap_certs_to_iwd_config(GKeyFile *      file,
+eap_certs_to_iwd_config(GKeyFile       *file,
                         NMSetting8021x *s_8021x,
                         bool            phase2,
-                        char *          iwd_prefix,
-                        GError **       error)
+                        char           *iwd_prefix,
+                        GError        **error)
 {
     NMSetting8021xCKScheme ca_cert_scheme =
         phase2 ? nm_setting_802_1x_get_phase2_ca_cert_scheme(s_8021x)
@@ -1043,13 +1043,13 @@ eap_certs_to_iwd_config(GKeyFile *      file,
                : nm_setting_802_1x_get_client_cert_scheme(s_8021x);
     NMSetting8021xCKScheme key_scheme;
     NMSettingSecretFlags   key_password_flags;
-    const char *           ca_path = phase2 ? nm_setting_802_1x_get_phase2_ca_path(s_8021x)
+    const char            *ca_path = phase2 ? nm_setting_802_1x_get_phase2_ca_path(s_8021x)
                                             : nm_setting_802_1x_get_ca_path(s_8021x);
-    const char *           cert_path;
-    const char *           key_path = NULL;
-    const char *           key_password;
-    const char *           domain_suffix_match;
-    const char *           domain_match;
+    const char            *cert_path;
+    const char            *key_path = NULL;
+    const char            *key_password;
+    const char            *domain_suffix_match;
+    const char            *domain_match;
     char                   setting_buf[128];
 
     /* TODO: should check that all certificates and the key are RSA */
@@ -1220,7 +1220,7 @@ private_key_done:
                                  : nm_setting_802_1x_get_domain_match(s_8021x);
 
     if (domain_suffix_match || domain_match) {
-        GString *   s = g_string_sized_new(128);
+        GString    *s = g_string_sized_new(128);
         const char *ptr;
         const char *end;
 
@@ -1287,13 +1287,13 @@ eap_optional_identity_to_iwd_config(GKeyFile *file, const char *iwd_prefix, cons
 }
 
 static gboolean
-eap_optional_password_to_iwd_config(GKeyFile *      file,
-                                    const char *    iwd_prefix,
+eap_optional_password_to_iwd_config(GKeyFile       *file,
+                                    const char     *iwd_prefix,
                                     NMSetting8021x *s_8021x,
-                                    GError **       error)
+                                    GError        **error)
 {
     char                 setting_buf[128];
-    const char *         password = nm_setting_802_1x_get_password(s_8021x);
+    const char          *password = nm_setting_802_1x_get_password(s_8021x);
     NMSettingSecretFlags flags    = nm_setting_802_1x_get_password_flags(s_8021x);
 
     if (!password && nm_setting_802_1x_get_password_raw(s_8021x)) {
@@ -1347,12 +1347,12 @@ eap_phase1_identity_to_iwd_config(GKeyFile *file, const char *iwd_prefix, NMSett
 }
 
 static gboolean
-eap_method_config_to_iwd_config(GKeyFile *      file,
+eap_method_config_to_iwd_config(GKeyFile       *file,
                                 NMSetting8021x *s_8021x,
                                 gboolean        phase2,
-                                const char *    method,
-                                const char *    iwd_prefix,
-                                GError **       error)
+                                const char     *method,
+                                const char     *iwd_prefix,
+                                GError        **error)
 {
     char prefix_buf[128];
 
@@ -1677,18 +1677,18 @@ ip6_config_to_iwd_config(GKeyFile *file, NMSettingIPConfig *s_ip, GError **error
 
 GKeyFile *
 nm_wifi_utils_connection_to_iwd_config(NMConnection *connection,
-                                       char **       out_filename,
-                                       GError **     error)
+                                       char        **out_filename,
+                                       GError      **error)
 {
-    NMSettingConnection * s_conn = nm_connection_get_setting_connection(connection);
-    NMSettingWireless *   s_wifi = nm_connection_get_setting_wireless(connection);
-    GBytes *              ssid;
-    const guint8 *        ssid_data;
-    gsize                 ssid_len;
-    NMIwdNetworkSecurity  security;
-    const char *          cloned_mac_addr;
-    gs_free char *        comment        = NULL;
-    nm_auto_unref_keyfile GKeyFile *file = NULL;
+    NMSettingConnection            *s_conn = nm_connection_get_setting_connection(connection);
+    NMSettingWireless              *s_wifi = nm_connection_get_setting_wireless(connection);
+    GBytes                         *ssid;
+    const guint8                   *ssid_data;
+    gsize                           ssid_len;
+    NMIwdNetworkSecurity            security;
+    const char                     *cloned_mac_addr;
+    gs_free char                   *comment = NULL;
+    nm_auto_unref_keyfile GKeyFile *file    = NULL;
 
     if (!s_conn || !s_wifi
         || !nm_streq(nm_setting_connection_get_connection_type(s_conn),
@@ -1800,3 +1800,151 @@ nm_wifi_utils_connection_to_iwd_config(NMConnection *connection,
 
     return g_steal_pointer(&file);
 }
+
+/* Wi-Fi Display Technical Specification v2.1.0 Table 27 */
+enum wfd_subelem_type {
+    WFD_SUBELEM_WFD_DEVICE_INFORMATION   = 0,
+    WFD_SUBELEM_ASSOCIATED_BSSID         = 1,
+    WFD_SUBELEM_COUPLED_SINK_INFORMATION = 6,
+    WFD_SUBELEM_EXTENDED_CAPABILITY      = 7,
+    WFD_SUBELEM_LOCAL_IP_ADDRESS         = 8,
+    WFD_SUBELEM_SESION_INFORMATION       = 9,
+    WFD_SUBELEM_ALTERNATIVE_MAC_ADDRESS  = 10,
+    WFD_SUBELEM_R2_DEVICE_INFORMATION    = 11,
+};
+
+bool
+nm_wifi_utils_parse_wfd_ies(GBytes *ies, NMIwdWfdInfo *out_wfd)
+{
+    size_t         len;
+    const uint8_t *data         = g_bytes_get_data(ies, &len);
+    const uint8_t *dev_info     = NULL;
+    uint16_t       dev_info_len = 0;
+    uint16_t       dev_info_flags;
+    const uint8_t *ext_capability     = NULL;
+    uint16_t       ext_capability_len = 0;
+
+    /* The single WFD IEs array provided by the client is supposed to be sent to
+     * the peer in the different frame types that may include the WFD IE: Probe
+     * Request/Response, Beacon, (Re)Association Request/Response, GO
+     * Negotiation Request/Response/Confirm and Provision Discovery
+     * Request/Response.
+     *
+     * It's going to be a subset of the elements allowed in all those frames.
+     * Validate that it contains at least a valid WFD Device Information (with
+     * the Session Available bit true) and that the sequence of subelements is
+     * valid.
+     */
+    while (len) {
+        uint8_t  subelem_id;
+        uint16_t subelem_len;
+
+        /* Does the subelement header fit */
+        if (len < 3)
+            return FALSE;
+
+        subelem_id  = data[0];
+        subelem_len = (data[1] << 8) | data[2];
+        data += 3;
+        len -= 3;
+
+        if (subelem_len > len)
+            return FALSE;
+
+        if (subelem_id == WFD_SUBELEM_WFD_DEVICE_INFORMATION) {
+            /* Is there a duplicate WFD Device Information */
+            if (dev_info)
+                return FALSE;
+
+            dev_info     = data;
+            dev_info_len = subelem_len;
+        }
+
+        if (subelem_id == WFD_SUBELEM_EXTENDED_CAPABILITY) {
+            /* Is there a duplicate WFD Extended Capability */
+            if (ext_capability)
+                return FALSE;
+
+            ext_capability     = data;
+            ext_capability_len = subelem_len;
+        }
+
+        data += subelem_len;
+        len -= subelem_len;
+    }
+
+    if (!dev_info || dev_info_len != 6)
+        return FALSE;
+
+    dev_info_flags = (dev_info[0] << 8) | dev_info[1];
+
+    /* Secondary sink not supported */
+    if ((dev_info_flags & 3) == 2)
+        return FALSE;
+
+    /* Must be available for WFD Session */
+    if (((dev_info_flags >> 4) & 3) != 1)
+        return FALSE;
+
+    /* TDLS persistent group re-invocation not supported */
+    if ((dev_info_flags >> 13) & 1)
+        return FALSE;
+
+    /* All other flags indicate support but not a requirement for something
+     * so not preserving them in the IEs IWD eventually sends doesn't break
+     * basic functionality.
+     */
+
+    if (ext_capability && ext_capability_len != 2)
+        return FALSE;
+
+    if (!out_wfd)
+        return TRUE;
+
+    out_wfd->source = NM_IN_SET(dev_info_flags & 3, 0, 3);
+    out_wfd->sink   = NM_IN_SET(dev_info_flags & 3, 1, 3);
+    out_wfd->port   = (dev_info[2] << 8) | dev_info[3];
+    out_wfd->has_audio =
+        out_wfd->sink ? ((dev_info_flags >> 10) & 1) == 0 : (((dev_info_flags >> 11) & 1) == 1);
+    out_wfd->has_uibc = ext_capability && (ext_capability[1] & 1) == 1;
+    out_wfd->has_cp   = ((dev_info_flags >> 8) & 1) == 1;
+    return TRUE;
+}
+
+GBytes *
+nm_wifi_utils_build_wfd_ies(const NMIwdWfdInfo *wfd)
+{
+    uint8_t  data[64];
+    uint8_t *ptr = data;
+
+    *ptr++ = WFD_SUBELEM_WFD_DEVICE_INFORMATION;
+    *ptr++ = 0; /* WFD Subelement length */
+    *ptr++ = 6;
+    *ptr++ = 0;                                               /* WFD Device Information bitmap: */
+    *ptr++ = (wfd->source ? (wfd->sink ? 3 : 0) : 1) | 0x10 | /* WFD Session Available */
+             (wfd->has_cp ? 0x100 : 0) | (wfd->has_audio ? 0 : 0x400);
+    *ptr++ = wfd->port >> 8;
+    *ptr++ = wfd->port & 255;
+    *ptr++ = 0; /* WFD Device Maximum throughput */
+    *ptr++ = 10;
+
+    if (wfd->has_uibc) {
+        *ptr++ = WFD_SUBELEM_EXTENDED_CAPABILITY;
+        *ptr++ = 0; /* WFD Subelement length */
+        *ptr++ = 2;
+        *ptr++ = 0x00; /* WFD Extended Capability Bitmap: */
+        *ptr++ = 0x10; /* UIBC Support */
+    }
+
+    return g_bytes_new(data, ptr - data);
+}
+
+bool
+nm_wifi_utils_wfd_info_eq(const NMIwdWfdInfo *a, const NMIwdWfdInfo *b)
+{
+    if (!a || !b)
+        return a == 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;
+}