diff options
| author | Michael Biebl <biebl@debian.org> | 2019-03-26 23:25:23 +0100 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2019-03-26 23:25:23 +0100 |
| commit | 9a6dcbf895f9da01768e64b73cec88c16157d91e (patch) | |
| tree | a359958930d731e9f1b59344642e10754419fe84 /src/supplicant | |
| parent | 964ae8cc391520440cf5aa13e2b9cc34850ea6c2 (diff) | |
New upstream version 1.16.0 upstream/1.16.0
Diffstat (limited to 'src/supplicant')
| -rw-r--r-- | src/supplicant/nm-supplicant-config.c | 101 | ||||
| -rw-r--r-- | src/supplicant/nm-supplicant-interface.c | 965 | ||||
| -rw-r--r-- | src/supplicant/nm-supplicant-interface.h | 42 | ||||
| -rw-r--r-- | src/supplicant/nm-supplicant-manager.c | 159 | ||||
| -rw-r--r-- | src/supplicant/nm-supplicant-manager.h | 5 | ||||
| -rw-r--r-- | src/supplicant/nm-supplicant-settings-verify.c | 20 | ||||
| -rw-r--r-- | src/supplicant/tests/meson.build | 3 | ||||
| -rw-r--r-- | src/supplicant/tests/test-supplicant-config.c | 72 |
8 files changed, 1238 insertions, 129 deletions
diff --git a/src/supplicant/nm-supplicant-config.c b/src/supplicant/nm-supplicant-config.c index cfb33008..7708224b 100644 --- a/src/supplicant/nm-supplicant-config.c +++ b/src/supplicant/nm-supplicant-config.c @@ -23,9 +23,10 @@ #include "nm-supplicant-config.h" -#include <string.h> #include <stdlib.h> +#include "nm-core-internal.h" + #include "nm-supplicant-settings-verify.h" #include "nm-setting.h" #include "nm-auth-subject.h" @@ -371,7 +372,6 @@ nm_supplicant_config_add_setting_macsec (NMSupplicantConfig * self, NMSettingMacsec * setting, GError **error) { - gs_unref_bytes GBytes *bytes = NULL; const char *value; char buf[32]; int port; @@ -395,43 +395,50 @@ nm_supplicant_config_add_setting_macsec (NMSupplicantConfig * self, } if (nm_setting_macsec_get_mode (setting) == NM_SETTING_MACSEC_MODE_PSK) { + guint8 buffer_cak[NM_SETTING_MACSEC_MKA_CAK_LENGTH/2]; + guint8 buffer_ckn[NM_SETTING_MACSEC_MKA_CKN_LENGTH/2]; + if (!nm_supplicant_config_add_option (self, "key_mgmt", "NONE", -1, NULL, error)) return FALSE; - /* CAK */ value = nm_setting_macsec_get_mka_cak (setting); - if (!value) { + if ( !value + || !nm_utils_hexstr2bin_buf (value, + FALSE, + FALSE, + NULL, + buffer_cak)) { g_set_error_literal (error, NM_SUPPLICANT_ERROR, NM_SUPPLICANT_ERROR_CONFIG, - "missing MKA CAK"); + value ? "invalid MKA CAK" : "missing MKA CAK"); return FALSE; } - - bytes = nm_utils_hexstr2bin (value); if (!nm_supplicant_config_add_option (self, "mka_cak", - g_bytes_get_data (bytes, NULL), - g_bytes_get_size (bytes), + (char *) buffer_cak, + sizeof (buffer_cak), "<hidden>", error)) return FALSE; - /* CKN */ value = nm_setting_macsec_get_mka_ckn (setting); - if (!value) { + if ( !value + || !nm_utils_hexstr2bin_buf (value, + FALSE, + FALSE, + NULL, + buffer_ckn)) { g_set_error_literal (error, NM_SUPPLICANT_ERROR, NM_SUPPLICANT_ERROR_CONFIG, - "missing MKA CKN"); + value ? "invalid MKA CKN" : "missing MKA CKN"); return FALSE; } - - bytes = nm_utils_hexstr2bin (value); if (!nm_supplicant_config_add_option (self, "mka_ckn", - g_bytes_get_data (bytes, NULL), - g_bytes_get_size (bytes), + (char *) buffer_ckn, + sizeof (buffer_ckn), NULL, error)) return FALSE; @@ -563,7 +570,7 @@ nm_supplicant_config_add_bgscan (NMSupplicantConfig *self, NM_SETTING_WIRELESS_MODE_ADHOC)) return TRUE; - /* Don't scan when the connection is locked to a specifc AP, since + /* Don't scan when the connection is locked to a specific AP, since * intra-ESS roaming (which requires periodic scanning) isn't being * used due to the specific AP lock. (bgo #513820) */ @@ -695,10 +702,16 @@ add_wep_key (NMSupplicantConfig *self, if ( (wep_type == NM_WEP_KEY_TYPE_UNKNOWN) || (wep_type == NM_WEP_KEY_TYPE_KEY)) { if ((key_len == 10) || (key_len == 26)) { - gs_unref_bytes GBytes *bytes = NULL; - - bytes = nm_utils_hexstr2bin (key); - if (!bytes) { + guint8 buffer[26/2]; + + if (!nm_utils_hexstr2bin_full (key, + FALSE, + FALSE, + NULL, + key_len / 2, + buffer, + sizeof (buffer), + NULL)) { g_set_error (error, NM_SUPPLICANT_ERROR, NM_SUPPLICANT_ERROR_CONFIG, "cannot add wep-key %s to suplicant config because key is not hex", name); @@ -706,8 +719,8 @@ add_wep_key (NMSupplicantConfig *self, } if (!nm_supplicant_config_add_option (self, name, - g_bytes_get_data (bytes, NULL), - g_bytes_get_size (bytes), + (char *) buffer, + key_len / 2, "<hidden>", error)) return FALSE; @@ -793,32 +806,41 @@ nm_supplicant_config_add_setting_wireless_security (NMSupplicantConfig *self, if (psk) { size_t psk_len = strlen (psk); - if (psk_len == 64) { - gs_unref_bytes GBytes *bytes = NULL; + + if (psk_len >= 8 && psk_len <= 63) { + /* Use TYPE_STRING here so that it gets pushed to the + * supplicant as a string, and therefore gets quoted, + * and therefore the supplicant will interpret it as a + * passphrase and not a hex key. + */ + if (!nm_supplicant_config_add_option_with_type (self, "psk", psk, -1, TYPE_STRING, "<hidden>", error)) + return FALSE; + } else if (nm_streq (key_mgmt, "sae")) { + /* If the SAE password doesn't comply with WPA-PSK limitation, + * we need to call it "sae_password" instead of "psk". + */ + if (!nm_supplicant_config_add_option_with_type (self, "sae_password", psk, -1, TYPE_STRING, "<hidden>", error)) + return FALSE; + } else if (psk_len == 64) { + guint8 buffer[32]; /* Hex PSK */ - bytes = nm_utils_hexstr2bin (psk); - if (!bytes) { + if (!nm_utils_hexstr2bin_buf (psk, + FALSE, + FALSE, + NULL, + buffer)) { g_set_error (error, NM_SUPPLICANT_ERROR, NM_SUPPLICANT_ERROR_CONFIG, "Cannot add psk to supplicant config due to invalid hex"); return FALSE; } - if (!nm_supplicant_config_add_option (self, "psk", - g_bytes_get_data (bytes, NULL), - g_bytes_get_size (bytes), + (char *) buffer, + sizeof (buffer), "<hidden>", error)) return FALSE; - } else if (psk_len >= 8 && psk_len <= 63) { - /* Use TYPE_STRING here so that it gets pushed to the - * supplicant as a string, and therefore gets quoted, - * and therefore the supplicant will interpret it as a - * passphrase and not a hex key. - */ - if (!nm_supplicant_config_add_option_with_type (self, "psk", psk, -1, TYPE_STRING, "<hidden>", error)) - return FALSE; } else { g_set_error (error, NM_SUPPLICANT_ERROR, NM_SUPPLICANT_ERROR_CONFIG, "Cannot add psk to supplicant config due to invalid PSK length %u (not between 8 and 63 characters)", @@ -845,7 +867,8 @@ nm_supplicant_config_add_setting_wireless_security (NMSupplicantConfig *self, /* Only WPA-specific things when using WPA */ if ( !strcmp (key_mgmt, "wpa-none") || !strcmp (key_mgmt, "wpa-psk") - || !strcmp (key_mgmt, "wpa-eap")) { + || !strcmp (key_mgmt, "wpa-eap") + || !strcmp (key_mgmt, "sae")) { if (!ADD_STRING_LIST_VAL (self, setting, wireless_security, proto, protos, "proto", ' ', TRUE, NULL, error)) return FALSE; if (!ADD_STRING_LIST_VAL (self, setting, wireless_security, pairwise, pairwise, "pairwise", ' ', TRUE, NULL, error)) diff --git a/src/supplicant/nm-supplicant-interface.c b/src/supplicant/nm-supplicant-interface.c index 0af9ebdb..f46689eb 100644 --- a/src/supplicant/nm-supplicant-interface.c +++ b/src/supplicant/nm-supplicant-interface.c @@ -22,21 +22,24 @@ #include "nm-default.h" #include "nm-supplicant-interface.h" +#include "nm-supplicant-manager.h" #include <stdio.h> -#include <string.h> #include "NetworkManagerUtils.h" #include "nm-supplicant-config.h" #include "nm-core-internal.h" #include "nm-dbus-compat.h" -#define WPAS_DBUS_IFACE_INTERFACE WPAS_DBUS_INTERFACE ".Interface" -#define WPAS_DBUS_IFACE_INTERFACE_WPS WPAS_DBUS_INTERFACE ".Interface.WPS" -#define WPAS_DBUS_IFACE_BSS WPAS_DBUS_INTERFACE ".BSS" -#define WPAS_DBUS_IFACE_NETWORK WPAS_DBUS_INTERFACE ".Network" -#define WPAS_ERROR_INVALID_IFACE WPAS_DBUS_INTERFACE ".InvalidInterface" -#define WPAS_ERROR_EXISTS_ERROR WPAS_DBUS_INTERFACE ".InterfaceExists" +#define WPAS_DBUS_IFACE_INTERFACE WPAS_DBUS_INTERFACE ".Interface" +#define WPAS_DBUS_IFACE_INTERFACE_WPS WPAS_DBUS_INTERFACE ".Interface.WPS" +#define WPAS_DBUS_IFACE_INTERFACE_P2P_DEVICE WPAS_DBUS_INTERFACE ".Interface.P2PDevice" +#define WPAS_DBUS_IFACE_BSS WPAS_DBUS_INTERFACE ".BSS" +#define WPAS_DBUS_IFACE_PEER WPAS_DBUS_INTERFACE ".Peer" +#define WPAS_DBUS_IFACE_GROUP WPAS_DBUS_INTERFACE ".Group" +#define WPAS_DBUS_IFACE_NETWORK WPAS_DBUS_INTERFACE ".Network" +#define WPAS_ERROR_INVALID_IFACE WPAS_DBUS_INTERFACE ".InvalidInterface" +#define WPAS_ERROR_EXISTS_ERROR WPAS_DBUS_INTERFACE ".InterfaceExists" /*****************************************************************************/ @@ -45,6 +48,11 @@ typedef struct { gulong change_id; } BssData; +typedef struct { + GDBusProxy *proxy; + gulong change_id; +} PeerData; + struct _AddNetworkData; typedef struct { @@ -74,26 +82,38 @@ typedef struct _AddNetworkData { } AddNetworkData; enum { - STATE, /* change in the interface's state */ - REMOVED, /* interface was removed by the supplicant */ - BSS_UPDATED, /* a new BSS appeared or an existing had properties changed */ - BSS_REMOVED, /* supplicant removed BSS from its scan list */ - SCAN_DONE, /* wifi scan is complete */ - CREDENTIALS_REQUEST, /* 802.1x identity or password requested */ - WPS_CREDENTIALS, /* WPS credentials received */ + STATE, /* change in the interface's state */ + REMOVED, /* interface was removed by the supplicant */ + BSS_UPDATED, /* a new BSS appeared or an existing had properties changed */ + BSS_REMOVED, /* supplicant removed BSS from its scan list */ + PEER_UPDATED, /* a new Peer appeared or an existing had properties changed */ + PEER_REMOVED, /* supplicant removed Peer from its scan list */ + SCAN_DONE, /* wifi scan is complete */ + CREDENTIALS_REQUEST, /* 802.1x identity or password requested */ + WPS_CREDENTIALS, /* WPS credentials received */ + GROUP_STARTED, /* a new Group (interface) was created */ + GROUP_FINISHED, /* a Group (interface) has been finished */ + GROUP_FORMATION_FAILURE, /* P2P Group formation failed */ LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = { 0 }; NM_GOBJECT_PROPERTIES_DEFINE (NMSupplicantInterface, PROP_IFACE, + PROP_OBJECT_PATH, + PROP_P2P_GROUP_JOINED, + PROP_P2P_GROUP_PATH, + PROP_P2P_GROUP_OWNER, PROP_SCANNING, PROP_CURRENT_BSS, PROP_DRIVER, + PROP_P2P_AVAILABLE, PROP_FAST_SUPPORT, PROP_AP_SUPPORT, PROP_PMF_SUPPORT, PROP_FILS_SUPPORT, + PROP_P2P_SUPPORT, + PROP_WFD_SUPPORT, ); typedef struct { @@ -104,6 +124,8 @@ typedef struct { NMSupplicantFeature ap_support; /* Lightweight AP mode support */ NMSupplicantFeature pmf_support; NMSupplicantFeature fils_support; + NMSupplicantFeature p2p_support; + NMSupplicantFeature wfd_support; guint32 max_scan_ssids; guint32 ready_count; @@ -120,6 +142,14 @@ typedef struct { GCancellable * init_cancellable; GDBusProxy * iface_proxy; GCancellable * other_cancellable; + GDBusProxy * p2p_proxy; + GDBusProxy * group_proxy; + + gboolean p2p_proxy_acquired; + gboolean group_proxy_acquired; + gboolean p2p_capable; + + gboolean p2p_group_owner; WpsData *wps_data; @@ -129,6 +159,8 @@ typedef struct { GHashTable * bss_proxies; char * current_bss; + GHashTable * peer_proxies; + gint64 last_scan; /* timestamp as returned by nm_utils_get_monotonic_timestamp_ms() */ } NMSupplicantInterfacePrivate; @@ -311,6 +343,122 @@ bss_add_new (NMSupplicantInterface *self, const char *object_path) self); } +static void +peer_data_destroy (gpointer user_data) +{ + PeerData *peer_data = user_data; + + nm_clear_g_signal_handler (peer_data->proxy, &peer_data->change_id); + g_object_unref (peer_data->proxy); + g_slice_free (PeerData, peer_data); +} + +static void +peer_proxy_properties_changed_cb (GDBusProxy *proxy, + GVariant *changed_properties, + char **invalidated_properties, + gpointer user_data) +{ + NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data); + + g_signal_emit (self, signals[PEER_UPDATED], 0, + g_dbus_proxy_get_object_path (proxy), + changed_properties); +} + +static GVariant * +peer_proxy_get_properties (NMSupplicantInterface *self, GDBusProxy *proxy) +{ + gs_strfreev char **properties = NULL; + GVariantBuilder builder; + char **iter; + + iter = properties = g_dbus_proxy_get_cached_property_names (proxy); + + g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}")); + if (iter) { + while (*iter) { + GVariant *copy = g_dbus_proxy_get_cached_property (proxy, *iter); + + g_variant_builder_add (&builder, "{sv}", *iter++, copy); + g_variant_unref (copy); + } + } + return g_variant_builder_end (&builder); +} + +static void +peer_proxy_acquired_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data) +{ + NMSupplicantInterface *self; + NMSupplicantInterfacePrivate *priv; + gs_free_error GError *error = NULL; + GVariant *props = NULL; + const char *object_path; + PeerData *peer_data; + gboolean success; + + success = g_async_initable_init_finish (G_ASYNC_INITABLE (proxy), result, &error); + if ( !success + && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + + self = NM_SUPPLICANT_INTERFACE (user_data); + priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + + if (!success) { + _LOGD ("failed to acquire Peer proxy: (%s)", error->message); + g_hash_table_remove (priv->peer_proxies, + g_dbus_proxy_get_object_path (proxy)); + return; + } + + object_path = g_dbus_proxy_get_object_path (proxy); + peer_data = g_hash_table_lookup (priv->peer_proxies, object_path); + if (!peer_data) + return; + + peer_data->change_id = g_signal_connect (proxy, "g-properties-changed", G_CALLBACK (peer_proxy_properties_changed_cb), self); + + props = peer_proxy_get_properties (self, proxy); + + g_signal_emit (self, signals[PEER_UPDATED], 0, + g_dbus_proxy_get_object_path (proxy), + g_variant_ref_sink (props)); + g_variant_unref (props); +} + +static void +peer_add_new (NMSupplicantInterface *self, const char *object_path) +{ + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + GDBusProxy *peer_proxy; + PeerData *peer_data; + + g_return_if_fail (object_path != NULL); + + if (g_hash_table_lookup (priv->peer_proxies, object_path)) + return; + + peer_proxy = g_object_new (G_TYPE_DBUS_PROXY, + "g-bus-type", G_BUS_TYPE_SYSTEM, + "g-flags", G_DBUS_PROXY_FLAGS_NONE, + "g-name", WPAS_DBUS_SERVICE, + "g-object-path", object_path, + "g-interface-name", WPAS_DBUS_IFACE_PEER, + NULL); + peer_data = g_slice_new0 (PeerData); + peer_data->proxy = peer_proxy; + g_hash_table_insert (priv->peer_proxies, + (char *) g_dbus_proxy_get_object_path (peer_proxy), + peer_data); + g_async_initable_init_async (G_ASYNC_INITABLE (peer_proxy), + G_PRIORITY_DEFAULT, + priv->other_cancellable, + (GAsyncReadyCallback) peer_proxy_acquired_cb, + self); +} + /*****************************************************************************/ static void @@ -450,12 +598,24 @@ static void parse_capabilities (NMSupplicantInterface *self, GVariant *capabilities) { NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); - gboolean have_active = FALSE, have_ssid = FALSE; + gboolean have_active = FALSE, have_p2p = FALSE, have_ssid = FALSE; gint32 max_scan_ssids = -1; const char **array; g_return_if_fail (capabilities && g_variant_is_of_type (capabilities, G_VARIANT_TYPE_VARDICT)); + if ( g_variant_lookup (capabilities, "Modes", "^a&s", &array) + && array) { + if (g_strv_contains (array, "p2p")) + have_p2p = TRUE; + g_free (array); + } + + if (priv->p2p_capable != have_p2p) { + priv->p2p_capable = have_p2p; + _notify (self, PROP_P2P_AVAILABLE); + } + if ( g_variant_lookup (capabilities, "Scan", "^a&s", &array) && array) { if (g_strv_contains (array, "active")) @@ -568,13 +728,35 @@ iface_set_pmf_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data) self = NM_SUPPLICANT_INTERFACE (user_data); - /* This can fail if the supplicant doesn't support PMF */ if (error) - _LOGD ("failed to set Pmf=1: %s", error->message); + _LOGW ("failed to set Pmf=1: %s", error->message); iface_check_ready (self); } +gboolean +nm_supplicant_interface_get_p2p_group_joined (NMSupplicantInterface *self) +{ + return NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)->group_proxy_acquired; +} + +const char* +nm_supplicant_interface_get_p2p_group_path (NMSupplicantInterface *self) +{ + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + + if (priv->group_proxy_acquired) + return g_dbus_proxy_get_object_path (priv->group_proxy); + else + return NULL; +} + +gboolean +nm_supplicant_interface_get_p2p_group_owner (NMSupplicantInterface *self) +{ + return NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)->p2p_group_owner; +} + NMSupplicantFeature nm_supplicant_interface_get_ap_support (NMSupplicantInterface *self) { @@ -593,6 +775,18 @@ nm_supplicant_interface_get_fils_support (NMSupplicantInterface *self) return NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)->fils_support; } +NMSupplicantFeature +nm_supplicant_interface_get_p2p_support (NMSupplicantInterface *self) +{ + return NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)->p2p_support; +} + +NMSupplicantFeature +nm_supplicant_interface_get_wfd_support (NMSupplicantInterface *self) +{ + return NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)->wfd_support; +} + void nm_supplicant_interface_set_ap_support (NMSupplicantInterface *self, NMSupplicantFeature ap_support) @@ -633,6 +827,24 @@ nm_supplicant_interface_set_fils_support (NMSupplicantInterface *self, priv->fils_support = fils_support; } +void +nm_supplicant_interface_set_p2p_support (NMSupplicantInterface *self, + NMSupplicantFeature p2p_support) +{ + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + + priv->p2p_support = p2p_support; +} + +void +nm_supplicant_interface_set_wfd_support (NMSupplicantInterface *self, + NMSupplicantFeature wfd_support) +{ + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + + priv->wfd_support = wfd_support; +} + /*****************************************************************************/ static void @@ -1120,10 +1332,260 @@ props_changed_cb (GDBusProxy *proxy, _LOGW ("connection disconnected (reason %d)", priv->disconnect_reason); } + /* We may not have priv->dev set yet if this interface was created from a + * known wpa_supplicant interface without knowing the device name. + */ + if (priv->dev == NULL && g_variant_lookup (changed_properties, "Ifname", "&s", &s)) { + priv->dev = g_strdup (s); + _notify (self, PROP_IFACE); + } + + g_object_thaw_notify (G_OBJECT (self)); +} + +static void +group_props_changed_cb (GDBusProxy *proxy, + GVariant *changed_properties, + char **invalidated_properties, + gpointer user_data) +{ + NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data); + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + char *s; + + g_object_freeze_notify (G_OBJECT (self)); + +#if 0 + v = g_variant_lookup_value (properties, "BSSID", G_VARIANT_TYPE_BYTESTRING); + if (v) { + bytes = g_variant_get_fixed_array (v, &len, 1); + if ( len == ETH_ALEN + && memcmp (bytes, nm_ip_addr_zero.addr_eth, ETH_ALEN) != 0 + && memcmp (bytes, (char[ETH_ALEN]) { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, ETH_ALEN) != 0) + nm_wifi_p2p_group_set_bssid_bin (group, bytes); + g_variant_unref (v); + } + + v = g_variant_lookup_value (properties, "SSID", G_VARIANT_TYPE_BYTESTRING); + if (v) { + bytes = g_variant_get_fixed_array (v, &len, 1); + len = MIN (32, len); + + /* Stupid ieee80211 layer uses <hidden> */ + if ( bytes && len + && !(((len == 8) || (len == 9)) && !memcmp (bytes, "<hidden>", 8)) + && !nm_utils_is_empty_ssid (bytes, len)) + nm_wifi_p2p_group_set_ssid (group, bytes, len); + + g_variant_unref (v); + } +#endif + + if (g_variant_lookup (changed_properties, "Role", "s", &s)) { + priv->p2p_group_owner = g_strcmp0 (s, "GO") == 0; + _notify (self, PROP_P2P_GROUP_OWNER); + g_free (s); + } + + /* NOTE: We do not seem to get any property change notifications for the Members + * property. However, we can keep track of these indirectly either by querying + * the groups that each peer is in or listening to the Join/Disconnect + * notifications. + */ + + g_object_thaw_notify (G_OBJECT (self)); +} + +static void +group_proxy_acquired_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data) +{ + NMSupplicantInterface *self; + NMSupplicantInterfacePrivate *priv; + gs_free_error GError *error = NULL; + gboolean success; + + success = g_async_initable_init_finish (G_ASYNC_INITABLE (proxy), result, &error); + if ( !success + && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + + self = NM_SUPPLICANT_INTERFACE (user_data); + priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + + if (!success) { + _LOGD ("failed to acquire Group proxy: (%s)", error->message); + g_clear_object (&priv->group_proxy); + return; + } + + priv->group_proxy_acquired = TRUE; + _notify (self, PROP_P2P_GROUP_JOINED); + _notify (self, PROP_P2P_GROUP_PATH); + + iface_check_ready (self); +} + +static void +p2p_props_changed_cb (GDBusProxy *proxy, + GVariant *changed_properties, + GStrv invalidated_properties, + gpointer user_data) +{ + NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data); + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + const char **array, **iter; + const char *path = NULL; + + g_object_freeze_notify (G_OBJECT (self)); + + if (g_variant_lookup (changed_properties, "Peers", "^a&o", &array)) { + iter = array; + while (*iter) + peer_add_new (self, *iter++); + g_free (array); + } + + if (g_variant_lookup (changed_properties, "Group", "&o", &path)) { + if (priv->group_proxy && g_strcmp0 (path, g_dbus_proxy_get_object_path (priv->group_proxy)) == 0) { + /* We already have the proxy, nothing to do. */ + } else if (path && g_strcmp0 (path, "/") != 0) { + if (priv->group_proxy != NULL) { + _LOGW ("P2P: Unexpected update of the group object path"); + priv->group_proxy_acquired = FALSE; + _notify (self, PROP_P2P_GROUP_JOINED); + _notify (self, PROP_P2P_GROUP_PATH); + g_clear_object (&priv->group_proxy); + } + + /* Delay ready state if we have not reached it yet. */ + if (priv->ready_count) + priv->ready_count++; + + priv->group_proxy = g_object_new (G_TYPE_DBUS_PROXY, + "g-bus-type", G_BUS_TYPE_SYSTEM, + "g-flags", G_DBUS_PROXY_FLAGS_NONE, + "g-name", WPAS_DBUS_SERVICE, + "g-object-path", path, + "g-interface-name", WPAS_DBUS_IFACE_GROUP, + NULL); + g_signal_connect (priv->group_proxy, "g-properties-changed", G_CALLBACK (group_props_changed_cb), self); + g_async_initable_init_async (G_ASYNC_INITABLE (priv->group_proxy), + G_PRIORITY_DEFAULT, + priv->other_cancellable, + (GAsyncReadyCallback) group_proxy_acquired_cb, + self); + } else { + priv->group_proxy_acquired = FALSE; + _notify (self, PROP_P2P_GROUP_JOINED); + _notify (self, PROP_P2P_GROUP_PATH); + g_clear_object (&priv->group_proxy); + } + } + g_object_thaw_notify (G_OBJECT (self)); } static void +p2p_device_found (GDBusProxy *proxy, + const char *path, + gpointer user_data) +{ + NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data); + + peer_add_new (self, path); +} + +static void +p2p_device_lost (GDBusProxy *proxy, + const char *path, + gpointer user_data) +{ + NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data); + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + PeerData *peer_data; + + peer_data = g_hash_table_lookup (priv->peer_proxies, path); + if (!peer_data) + return; + g_hash_table_steal (priv->peer_proxies, path); + g_signal_emit (self, signals[PEER_REMOVED], 0, path); + peer_data_destroy (peer_data); +} + +static void +p2p_group_started (GDBusProxy *proxy, + GVariant *params, + gpointer user_data) +{ + NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data); + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + NMSupplicantInterface *iface = NULL; + char *group_path = NULL; + char *iface_path = NULL; + + /* There is one more parameter: the role, but we don't really care about that here. */ + if (!g_variant_lookup (params, "group_object", "&o", &group_path)) { + _LOGW ("P2P: GroupStarted signal is missing the \"group_object\" parameter"); + return; + } + + if (!g_variant_lookup (params, "interface_object", "&o", &iface_path)) { + _LOGW ("P2P: GroupStarted signal is missing the \"interface\" parameter"); + return; + } + + if (g_strcmp0 (iface_path, priv->object_path) == 0) { + _LOGW ("P2P: GroupStarted on existing interface"); + iface = g_object_ref (self); + } else { + iface = nm_supplicant_manager_create_interface_from_path (nm_supplicant_manager_get (), + iface_path); + if (iface == NULL) { + _LOGW ("P2P: Group interface already exists in GroupStarted handler, aborting further processing."); + return; + } + } + + /* Signal existence of the (new) interface. */ + g_signal_emit (self, signals[GROUP_STARTED], 0, iface); + g_object_unref (iface); +} + +static void +p2p_group_formation_failure (GDBusProxy *proxy, + const char *group, + gpointer user_data) +{ + NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data); + + g_signal_emit (self, signals[GROUP_FORMATION_FAILURE], 0, group); +} + +static void +p2p_group_finished (GDBusProxy *proxy, + GVariant *params, + gpointer user_data) +{ + NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data); + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + const char *iface_path = NULL; + /* TODO: Group finished is called on the management interface! + * This means the signal consumer will currently need to assume which + * interface is finishing or it needs to match the object paths. + */ + + if (!g_variant_lookup (params, "interface_object", "&o", &iface_path)) { + _LOGW ("P2P: GroupFinished signal is missing the \"interface\" parameter"); + return; + } + + _LOGD ("P2P: GroupFinished signal on interface %s for interface %s", priv->object_path, iface_path); + + /* Signal group finish interface (on management interface). */ + g_signal_emit (self, signals[GROUP_FINISHED], 0, iface_path); +} + +static void on_iface_proxy_acquired (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data) { NMSupplicantInterface *self; @@ -1175,22 +1637,9 @@ on_iface_proxy_acquired (GDBusProxy *proxy, GAsyncResult *result, gpointer user_ NULL, NULL); - /* Initialize global PMF setting to 'optional' */ - priv->ready_count = 1; - g_dbus_proxy_call (priv->iface_proxy, - DBUS_INTERFACE_PROPERTIES ".Set", - g_variant_new ("(ssv)", - WPAS_DBUS_IFACE_INTERFACE, - "Pmf", - g_variant_new_string ("1")), - G_DBUS_CALL_FLAGS_NONE, - -1, - priv->init_cancellable, - (GAsyncReadyCallback) iface_set_pmf_cb, - self); - - /* Check whether NetworkReply and AP mode are supported */ - priv->ready_count++; + /* Check whether NetworkReply and AP mode are supported. + * ready_count was initialized to 1 in interface_add_done(). + */ g_dbus_proxy_call (priv->iface_proxy, "NetworkReply", g_variant_new ("(oss)", @@ -1203,6 +1652,22 @@ on_iface_proxy_acquired (GDBusProxy *proxy, GAsyncResult *result, gpointer user_ (GAsyncReadyCallback) iface_check_netreply_cb, self); + if (priv->pmf_support == NM_SUPPLICANT_FEATURE_YES) { + /* Initialize global PMF setting to 'optional' */ + priv->ready_count++; + g_dbus_proxy_call (priv->iface_proxy, + DBUS_INTERFACE_PROPERTIES ".Set", + g_variant_new ("(ssv)", + WPAS_DBUS_IFACE_INTERFACE, + "Pmf", + g_variant_new_string ("1")), + G_DBUS_CALL_FLAGS_NONE, + -1, + priv->init_cancellable, + (GAsyncReadyCallback) iface_set_pmf_cb, + self); + } + if (priv->ap_support == NM_SUPPLICANT_FEATURE_UNKNOWN) { /* If the global supplicant capabilities property is not present, we can * fall back to checking whether the ProbeRequest method is supported. If @@ -1222,13 +1687,63 @@ on_iface_proxy_acquired (GDBusProxy *proxy, GAsyncResult *result, gpointer user_ } static void +on_p2p_proxy_acquired (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data) +{ + NMSupplicantInterface *self; + NMSupplicantInterfacePrivate *priv; + gs_free_error GError *error = NULL; + + if (!g_async_initable_init_finish (G_ASYNC_INITABLE (proxy), result, &error)) { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { + self = NM_SUPPLICANT_INTERFACE (user_data); + priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + _LOGW ("failed to acquire wpa_supplicant p2p proxy: (%s)", error->message); + + g_clear_object (&priv->p2p_proxy); + + iface_check_ready (self); + } + return; + } + + self = NM_SUPPLICANT_INTERFACE (user_data); + priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + + _nm_dbus_signal_connect (priv->p2p_proxy, "DeviceFound", G_VARIANT_TYPE ("(o)"), + G_CALLBACK (p2p_device_found), self); + _nm_dbus_signal_connect (priv->p2p_proxy, "DeviceLost", G_VARIANT_TYPE ("(o)"), + G_CALLBACK (p2p_device_lost), self); + _nm_dbus_signal_connect (priv->p2p_proxy, "GroupStarted", G_VARIANT_TYPE ("(a{sv})"), + G_CALLBACK (p2p_group_started), self); + _nm_dbus_signal_connect (priv->p2p_proxy, "GroupFormationFailure", G_VARIANT_TYPE ("(s)"), + G_CALLBACK (p2p_group_formation_failure), self); + _nm_dbus_signal_connect (priv->p2p_proxy, "GroupFinished", G_VARIANT_TYPE ("(a{sv})"), + G_CALLBACK (p2p_group_finished), self); + /* TODO: + * * WpsFailed + * * FindStopped + * * GONegotationFailure + * * InvitationReceived + */ + + priv->p2p_proxy_acquired = TRUE; + _notify (self, PROP_P2P_AVAILABLE); + + iface_check_ready (self); +} + +static void interface_add_done (NMSupplicantInterface *self, const char *path) { NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); _LOGD ("interface added to supplicant"); + /* Iface ready check happens in iface_check_netreply_cb */ + priv->ready_count = 1; + priv->object_path = g_strdup (path); + _notify (self, PROP_OBJECT_PATH); priv->iface_proxy = g_object_new (G_TYPE_DBUS_PROXY, "g-bus-type", G_BUS_TYPE_SYSTEM, "g-flags", G_DBUS_PROXY_FLAGS_NONE, @@ -1242,6 +1757,23 @@ interface_add_done (NMSupplicantInterface *self, const char *path) priv->init_cancellable, (GAsyncReadyCallback) on_iface_proxy_acquired, self); + + if (priv->p2p_support == NM_SUPPLICANT_FEATURE_YES) { + priv->ready_count++; + priv->p2p_proxy = g_object_new (G_TYPE_DBUS_PROXY, + "g-bus-type", G_BUS_TYPE_SYSTEM, + "g-flags", G_DBUS_PROXY_FLAGS_NONE, + "g-name", WPAS_DBUS_SERVICE, + "g-object-path", priv->object_path, + "g-interface-name", WPAS_DBUS_IFACE_INTERFACE_P2P_DEVICE, + NULL); + g_signal_connect (priv->p2p_proxy, "g-properties-changed", G_CALLBACK (p2p_props_changed_cb), self); + g_async_initable_init_async (G_ASYNC_INITABLE (priv->p2p_proxy), + G_PRIORITY_DEFAULT, + priv->init_cancellable, + (GAsyncReadyCallback) on_p2p_proxy_acquired, + self); + } } static void @@ -1323,6 +1855,39 @@ interface_add_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data) } } +static void +interface_removed_cb (GDBusProxy *proxy, + const char *path, + gpointer user_data) +{ + NMSupplicantInterface *self; + NMSupplicantInterfacePrivate *priv; + + self = NM_SUPPLICANT_INTERFACE (user_data); + priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + + if (g_strcmp0 (priv->object_path, path) != 0) + return; + + _LOGD ("Received interface removed signal"); + + /* The interface may lose its last reference during signal handling otherwise. */ + g_object_ref (self); + + /* Invalidate the object path to prevent the manager from trying to remove + * a non-existing interface. */ + g_clear_pointer (&priv->object_path, g_free); + _notify (self, PROP_OBJECT_PATH); + + /* No need to clean up everything now, that will happen at dispose time. */ + + /* Interface is down and has been removed. */ + set_state (self, NM_SUPPLICANT_INTERFACE_STATE_DOWN); + g_signal_emit (self, signals[REMOVED], 0); + + g_object_unref (self); +} + #if HAVE_WEXT #define DEFAULT_WIFI_DRIVER "nl80211,wext" #else @@ -1337,7 +1902,6 @@ on_wpas_proxy_acquired (GDBusProxy *proxy, GAsyncResult *result, gpointer user_d gs_free_error GError *error = NULL; GDBusProxy *wpas_proxy; GVariantBuilder props; - const char *driver_name = NULL; wpas_proxy = g_dbus_proxy_new_for_bus_finish (result, &error); if (!wpas_proxy) { @@ -1354,41 +1918,53 @@ on_wpas_proxy_acquired (GDBusProxy *proxy, GAsyncResult *result, gpointer user_d priv->wpas_proxy = wpas_proxy; + /* Watch for interface removal. */ + _nm_dbus_signal_connect (priv->wpas_proxy, "InterfaceRemoved", G_VARIANT_TYPE ("(o)"), + G_CALLBACK (interface_removed_cb), self); + /* Try to add the interface to the supplicant. If the supplicant isn't * running, this will start it via D-Bus activation and return the response * when the supplicant has started. */ - switch (priv->driver) { - case NM_SUPPLICANT_DRIVER_WIRELESS: - driver_name = DEFAULT_WIFI_DRIVER; - break; - case NM_SUPPLICANT_DRIVER_WIRED: - driver_name = "wired"; - break; - case NM_SUPPLICANT_DRIVER_MACSEC: - driver_name = "macsec_linux"; - break; - } + if (priv->dev != NULL) { + const char *driver_name = NULL; + + switch (priv->driver) { + case NM_SUPPLICANT_DRIVER_WIRELESS: + driver_name = DEFAULT_WIFI_DRIVER; + break; + case NM_SUPPLICANT_DRIVER_WIRED: + driver_name = "wired"; + break; + case NM_SUPPLICANT_DRIVER_MACSEC: + driver_name = "macsec_linux"; + break; + } - g_return_if_fail (driver_name); + g_return_if_fail (driver_name); - g_variant_builder_init (&props, G_VARIANT_TYPE_VARDICT); - g_variant_builder_add (&props, "{sv}", - "Driver", - g_variant_new_string (driver_name)); - g_variant_builder_add (&props, "{sv}", - "Ifname", - g_variant_new_string (priv->dev)); + g_variant_builder_init (&props, G_VARIANT_TYPE_VARDICT); + g_variant_builder_add (&props, "{sv}", + "Driver", + g_variant_new_string (driver_name)); + g_variant_builder_add (&props, "{sv}", + "Ifname", + g_variant_new_string (priv->dev)); - g_dbus_proxy_call (priv->wpas_proxy, - "CreateInterface", - g_variant_new ("(a{sv})", &props), - G_DBUS_CALL_FLAGS_NONE, - -1, - priv->init_cancellable, - (GAsyncReadyCallback) interface_add_cb, - self); + g_dbus_proxy_call (priv->wpas_proxy, + "CreateInterface", + g_variant_new ("(a{sv})", &props), + G_DBUS_CALL_FLAGS_NONE, + -1, + priv->init_cancellable, + (GAsyncReadyCallback) interface_add_cb, + self); + } else if (priv->object_path) { + interface_add_done (self, priv->object_path); + } else { + g_assert_not_reached (); + } } static void @@ -1408,8 +1984,7 @@ interface_add (NMSupplicantInterface *self) priv->init_cancellable = g_cancellable_new (); g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM, - G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | - G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS, + G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES, NULL, WPAS_DBUS_SERVICE, WPAS_DBUS_PATH, @@ -1897,6 +2472,141 @@ nm_supplicant_interface_get_max_scan_ssids (NMSupplicantInterface *self) /*****************************************************************************/ +void +nm_supplicant_interface_p2p_start_find (NMSupplicantInterface *self, + guint timeout) +{ + NMSupplicantInterfacePrivate *priv; + GVariantBuilder builder; + + g_return_if_fail (NM_IS_SUPPLICANT_INTERFACE (self)); + g_return_if_fail (timeout > 0 && timeout <= 600); + + priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + + g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT); + g_variant_builder_add (&builder, "{sv}", "Timeout", g_variant_new_int32 (timeout)); + + g_dbus_proxy_call (priv->p2p_proxy, + "Find", + g_variant_new ("(a{sv})", &builder), + G_DBUS_CALL_FLAGS_NONE, + -1, + priv->other_cancellable, + (GAsyncReadyCallback) log_result_cb, + self); +} + +void +nm_supplicant_interface_p2p_stop_find (NMSupplicantInterface *self) +{ + NMSupplicantInterfacePrivate *priv; + + g_return_if_fail (NM_IS_SUPPLICANT_INTERFACE (self)); + + priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + + g_dbus_proxy_call (priv->p2p_proxy, + "StopFind", + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + priv->other_cancellable, + (GAsyncReadyCallback) scan_request_cb, + self); +} + +/*****************************************************************************/ + +void +nm_supplicant_interface_p2p_connect (NMSupplicantInterface * self, + const char * peer, + const char * wps_method, + const char * wps_pin) +{ + NMSupplicantInterfacePrivate *priv; + GVariantBuilder builder; + + g_return_if_fail (NM_IS_SUPPLICANT_INTERFACE (self)); + + priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + + /* Don't do anything if there is no connection to the supplicant yet. */ + if (!priv->p2p_proxy || !priv->object_path) + return; + + /* Connect parameters */ + g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT); + + g_variant_builder_add (&builder, "{sv}", "wps_method", g_variant_new_string (wps_method)); + + if (wps_pin) + g_variant_builder_add (&builder, "{sv}", "pin", g_variant_new_string (wps_pin)); + + g_variant_builder_add (&builder, "{sv}", "peer", g_variant_new_object_path (peer)); + + g_variant_builder_add (&builder, "{sv}", "join", g_variant_new_boolean (FALSE)); + g_variant_builder_add (&builder, "{sv}", "persistent", g_variant_new_boolean (FALSE)); + g_variant_builder_add (&builder, "{sv}", "go_intent", g_variant_new_int32 (7)); + + g_dbus_proxy_call (priv->p2p_proxy, + "Connect", + g_variant_new ("(a{sv})", &builder), + G_DBUS_CALL_FLAGS_NONE, + -1, + priv->other_cancellable, + (GAsyncReadyCallback) log_result_cb, + "p2p connect"); +} + +void +nm_supplicant_interface_p2p_cancel_connect (NMSupplicantInterface * self) +{ + NMSupplicantInterfacePrivate *priv; + + g_return_if_fail (NM_IS_SUPPLICANT_INTERFACE (self)); + + priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + + /* Don't do anything if there is no connection to the supplicant yet. */ + if (!priv->p2p_proxy || !priv->object_path) + return; + + g_dbus_proxy_call (priv->p2p_proxy, + "Cancel", + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + priv->other_cancellable, + (GAsyncReadyCallback) log_result_cb, + "cancel p2p connect"); +} + +void +nm_supplicant_interface_p2p_disconnect (NMSupplicantInterface * self) +{ + NMSupplicantInterfacePrivate *priv; + + g_return_if_fail (NM_IS_SUPPLICANT_INTERFACE (self)); + + priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + + /* Don't do anything if there is no connection to the supplicant. */ + if (!priv->p2p_proxy || !priv->object_path) + return; + + g_dbus_proxy_call (priv->p2p_proxy, + "Disconnect", + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + priv->other_cancellable, + (GAsyncReadyCallback) log_result_cb, + "p2p disconnect"); +} + +/*****************************************************************************/ + static void get_property (GObject *object, guint prop_id, @@ -1912,6 +2622,18 @@ get_property (GObject *object, case PROP_CURRENT_BSS: g_value_set_string (value, priv->current_bss); break; + case PROP_P2P_GROUP_JOINED: + g_value_set_boolean (value, priv->p2p_capable && priv->group_proxy_acquired); + break; + case PROP_P2P_GROUP_PATH: + g_value_set_string (value, nm_supplicant_interface_get_p2p_group_path (NM_SUPPLICANT_INTERFACE (object))); + break; + case PROP_P2P_GROUP_OWNER: + g_value_set_boolean (value, priv->p2p_group_owner); + break; + case PROP_P2P_AVAILABLE: + g_value_set_boolean (value, priv->p2p_capable && priv->p2p_proxy_acquired); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1930,7 +2652,10 @@ set_property (GObject *object, case PROP_IFACE: /* construct-only */ priv->dev = g_value_dup_string (value); - g_return_if_fail (priv->dev); + break; + case PROP_OBJECT_PATH: + /* construct-only */ + priv->object_path = g_value_dup_string (value); break; case PROP_DRIVER: /* construct-only */ @@ -1952,6 +2677,14 @@ set_property (GObject *object, /* construct-only */ priv->fils_support = g_value_get_int (value); break; + case PROP_P2P_SUPPORT: + /* construct-only */ + priv->p2p_support = g_value_get_int (value); + break; + case PROP_WFD_SUPPORT: + /* construct-only */ + priv->wfd_support = g_value_get_int (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1965,25 +2698,34 @@ nm_supplicant_interface_init (NMSupplicantInterface * self) priv->state = NM_SUPPLICANT_INTERFACE_STATE_INIT; priv->bss_proxies = g_hash_table_new_full (nm_str_hash, g_str_equal, NULL, bss_data_destroy); + priv->peer_proxies = g_hash_table_new_full (nm_str_hash, g_str_equal, NULL, peer_data_destroy); } NMSupplicantInterface * nm_supplicant_interface_new (const char *ifname, + const char *object_path, NMSupplicantDriver driver, NMSupplicantFeature fast_support, NMSupplicantFeature ap_support, NMSupplicantFeature pmf_support, - NMSupplicantFeature fils_support) + NMSupplicantFeature fils_support, + NMSupplicantFeature p2p_support, + NMSupplicantFeature wfd_support) { - g_return_val_if_fail (ifname != NULL, NULL); + /* One of ifname or path need to be set */ + g_return_val_if_fail (ifname != NULL || object_path != NULL, NULL); + g_return_val_if_fail (ifname == NULL || object_path == NULL, NULL); return g_object_new (NM_TYPE_SUPPLICANT_INTERFACE, NM_SUPPLICANT_INTERFACE_IFACE, ifname, + NM_SUPPLICANT_INTERFACE_OBJECT_PATH, object_path, NM_SUPPLICANT_INTERFACE_DRIVER, (guint) driver, NM_SUPPLICANT_INTERFACE_FAST_SUPPORT, (int) fast_support, NM_SUPPLICANT_INTERFACE_AP_SUPPORT, (int) ap_support, NM_SUPPLICANT_INTERFACE_PMF_SUPPORT, (int) pmf_support, NM_SUPPLICANT_INTERFACE_FILS_SUPPORT, (int) fils_support, + NM_SUPPLICANT_INTERFACE_P2P_SUPPORT, (int) p2p_support, + NM_SUPPLICANT_INTERFACE_WFD_SUPPORT, (int) wfd_support, NULL); } @@ -2013,12 +2755,21 @@ dispose (GObject *object) if (priv->iface_proxy) g_signal_handlers_disconnect_by_data (priv->iface_proxy, object); g_clear_object (&priv->iface_proxy); + if (priv->p2p_proxy) + g_signal_handlers_disconnect_by_data (priv->p2p_proxy, object); + g_clear_object (&priv->p2p_proxy); + if (priv->group_proxy) + g_signal_handlers_disconnect_by_data (priv->group_proxy, object); + g_clear_object (&priv->group_proxy); nm_clear_g_cancellable (&priv->init_cancellable); nm_clear_g_cancellable (&priv->other_cancellable); + if (priv->wpas_proxy) + g_signal_handlers_disconnect_by_data (priv->wpas_proxy, object); g_clear_object (&priv->wpas_proxy); g_clear_pointer (&priv->bss_proxies, g_hash_table_destroy); + g_clear_pointer (&priv->peer_proxies, g_hash_table_destroy); g_clear_pointer (&priv->net_path, g_free); g_clear_pointer (&priv->dev, g_free); @@ -2053,12 +2804,38 @@ nm_supplicant_interface_class_init (NMSupplicantInterfaceClass *klass) G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + obj_properties[PROP_OBJECT_PATH] = + g_param_spec_string (NM_SUPPLICANT_INTERFACE_OBJECT_PATH, "", "", + NULL, + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + obj_properties[PROP_P2P_GROUP_JOINED] = + g_param_spec_boolean (NM_SUPPLICANT_INTERFACE_P2P_GROUP_JOINED, "", "", + FALSE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); + obj_properties[PROP_P2P_GROUP_PATH] = + g_param_spec_string (NM_SUPPLICANT_INTERFACE_P2P_GROUP_PATH, "", "", + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); + obj_properties[PROP_P2P_GROUP_OWNER] = + g_param_spec_boolean (NM_SUPPLICANT_INTERFACE_P2P_GROUP_OWNER, "", "", + FALSE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); obj_properties[PROP_DRIVER] = g_param_spec_uint (NM_SUPPLICANT_INTERFACE_DRIVER, "", "", 0, G_MAXUINT, NM_SUPPLICANT_DRIVER_WIRELESS, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + obj_properties[PROP_P2P_AVAILABLE] = + g_param_spec_boolean (NM_SUPPLICANT_INTERFACE_P2P_AVAILABLE, "", "", + FALSE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); obj_properties[PROP_FAST_SUPPORT] = g_param_spec_int (NM_SUPPLICANT_INTERFACE_FAST_SUPPORT, "", "", NM_SUPPLICANT_FEATURE_UNKNOWN, @@ -2091,6 +2868,22 @@ nm_supplicant_interface_class_init (NMSupplicantInterfaceClass *klass) G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + obj_properties[PROP_P2P_SUPPORT] = + g_param_spec_int (NM_SUPPLICANT_INTERFACE_P2P_SUPPORT, "", "", + NM_SUPPLICANT_FEATURE_UNKNOWN, + NM_SUPPLICANT_FEATURE_YES, + NM_SUPPLICANT_FEATURE_UNKNOWN, + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + obj_properties[PROP_WFD_SUPPORT] = + g_param_spec_int (NM_SUPPLICANT_INTERFACE_WFD_SUPPORT, "", "", + NM_SUPPLICANT_FEATURE_UNKNOWN, + NM_SUPPLICANT_FEATURE_YES, + NM_SUPPLICANT_FEATURE_UNKNOWN, + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties); @@ -2126,6 +2919,22 @@ nm_supplicant_interface_class_init (NMSupplicantInterfaceClass *klass) NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_STRING); + signals[PEER_UPDATED] = + g_signal_new (NM_SUPPLICANT_INTERFACE_PEER_UPDATED, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, NULL, + G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_VARIANT); + + signals[PEER_REMOVED] = + g_signal_new (NM_SUPPLICANT_INTERFACE_PEER_REMOVED, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, NULL, + G_TYPE_NONE, 1, G_TYPE_STRING); + signals[SCAN_DONE] = g_signal_new (NM_SUPPLICANT_INTERFACE_SCAN_DONE, G_OBJECT_CLASS_TYPE (object_class), @@ -2149,4 +2958,28 @@ nm_supplicant_interface_class_init (NMSupplicantInterfaceClass *klass) 0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_VARIANT); + + signals[GROUP_STARTED] = + g_signal_new (NM_SUPPLICANT_INTERFACE_GROUP_STARTED, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, NULL, + G_TYPE_NONE, 1, NM_TYPE_SUPPLICANT_INTERFACE); + + signals[GROUP_FINISHED] = + g_signal_new (NM_SUPPLICANT_INTERFACE_GROUP_FINISHED, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, NULL, + G_TYPE_NONE, 1, G_TYPE_STRING); + + signals[GROUP_FORMATION_FAILURE] = + g_signal_new (NM_SUPPLICANT_INTERFACE_GROUP_FORMATION_FAILURE, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, NULL, + G_TYPE_NONE, 1, G_TYPE_VARIANT); } diff --git a/src/supplicant/nm-supplicant-interface.h b/src/supplicant/nm-supplicant-interface.h index 0365fdcd..0aa7732a 100644 --- a/src/supplicant/nm-supplicant-interface.h +++ b/src/supplicant/nm-supplicant-interface.h @@ -55,33 +55,48 @@ typedef enum { /* Properties */ #define NM_SUPPLICANT_INTERFACE_IFACE "iface" +#define NM_SUPPLICANT_INTERFACE_OBJECT_PATH "object-path" #define NM_SUPPLICANT_INTERFACE_SCANNING "scanning" #define NM_SUPPLICANT_INTERFACE_CURRENT_BSS "current-bss" +#define NM_SUPPLICANT_INTERFACE_P2P_GROUP_JOINED "p2p-group-joined" +#define NM_SUPPLICANT_INTERFACE_P2P_GROUP_PATH "p2p-group-path" +#define NM_SUPPLICANT_INTERFACE_P2P_GROUP_OWNER "p2p-group-owner" #define NM_SUPPLICANT_INTERFACE_DRIVER "driver" +#define NM_SUPPLICANT_INTERFACE_P2P_AVAILABLE "p2p-available" #define NM_SUPPLICANT_INTERFACE_FAST_SUPPORT "fast-support" #define NM_SUPPLICANT_INTERFACE_AP_SUPPORT "ap-support" #define NM_SUPPLICANT_INTERFACE_PMF_SUPPORT "pmf-support" #define NM_SUPPLICANT_INTERFACE_FILS_SUPPORT "fils-support" +#define NM_SUPPLICANT_INTERFACE_P2P_SUPPORT "p2p-support" +#define NM_SUPPLICANT_INTERFACE_WFD_SUPPORT "wfd-support" /* Signals */ #define NM_SUPPLICANT_INTERFACE_STATE "state" #define NM_SUPPLICANT_INTERFACE_REMOVED "removed" #define NM_SUPPLICANT_INTERFACE_BSS_UPDATED "bss-updated" #define NM_SUPPLICANT_INTERFACE_BSS_REMOVED "bss-removed" +#define NM_SUPPLICANT_INTERFACE_PEER_UPDATED "peer-updated" +#define NM_SUPPLICANT_INTERFACE_PEER_REMOVED "peer-removed" #define NM_SUPPLICANT_INTERFACE_SCAN_DONE "scan-done" #define NM_SUPPLICANT_INTERFACE_CREDENTIALS_REQUEST "credentials-request" #define NM_SUPPLICANT_INTERFACE_WPS_CREDENTIALS "wps-credentials" +#define NM_SUPPLICANT_INTERFACE_GROUP_STARTED "group-started" +#define NM_SUPPLICANT_INTERFACE_GROUP_FINISHED "group-finished" +#define NM_SUPPLICANT_INTERFACE_GROUP_FORMATION_FAILURE "group-formation-failure" typedef struct _NMSupplicantInterfaceClass NMSupplicantInterfaceClass; GType nm_supplicant_interface_get_type (void); NMSupplicantInterface * nm_supplicant_interface_new (const char *ifname, + const char *object_path, NMSupplicantDriver driver, NMSupplicantFeature fast_support, NMSupplicantFeature ap_support, NMSupplicantFeature pmf_support, - NMSupplicantFeature fils_support); + NMSupplicantFeature fils_support, + NMSupplicantFeature p2p_support, + NMSupplicantFeature wfd_support); void nm_supplicant_interface_set_supplicant_available (NMSupplicantInterface *self, gboolean available); @@ -120,14 +135,33 @@ guint nm_supplicant_interface_get_max_scan_ssids (NMSupplicantInterface *self); gboolean nm_supplicant_interface_get_has_credentials_request (NMSupplicantInterface *self); +gboolean nm_supplicant_interface_get_p2p_group_joined (NMSupplicantInterface *self); + +const char* nm_supplicant_interface_get_p2p_group_path (NMSupplicantInterface *self); + +gboolean nm_supplicant_interface_get_p2p_group_owner (NMSupplicantInterface *self); + gboolean nm_supplicant_interface_credentials_reply (NMSupplicantInterface *self, const char *field, const char *value, GError **error); +void nm_supplicant_interface_p2p_start_find (NMSupplicantInterface *self, + guint timeout); +void nm_supplicant_interface_p2p_stop_find (NMSupplicantInterface *self); + +void nm_supplicant_interface_p2p_connect (NMSupplicantInterface * self, + const char * peer, + const char * wps_method, + const char * wps_pin); +void nm_supplicant_interface_p2p_cancel_connect (NMSupplicantInterface * self); +void nm_supplicant_interface_p2p_disconnect (NMSupplicantInterface * self); + NMSupplicantFeature nm_supplicant_interface_get_ap_support (NMSupplicantInterface *self); NMSupplicantFeature nm_supplicant_interface_get_pmf_support (NMSupplicantInterface *self); NMSupplicantFeature nm_supplicant_interface_get_fils_support (NMSupplicantInterface *self); +NMSupplicantFeature nm_supplicant_interface_get_p2p_support (NMSupplicantInterface *self); +NMSupplicantFeature nm_supplicant_interface_get_wfd_support (NMSupplicantInterface *self); void nm_supplicant_interface_set_ap_support (NMSupplicantInterface *self, NMSupplicantFeature apmode); @@ -141,6 +175,12 @@ void nm_supplicant_interface_set_pmf_support (NMSupplicantInterface *self, void nm_supplicant_interface_set_fils_support (NMSupplicantInterface *self, NMSupplicantFeature fils_support); +void nm_supplicant_interface_set_p2p_support (NMSupplicantInterface *self, + NMSupplicantFeature p2p_support); + +void nm_supplicant_interface_set_wfd_support (NMSupplicantInterface *self, + NMSupplicantFeature wfd_support); + void nm_supplicant_interface_enroll_wps (NMSupplicantInterface *self, const char *const type, const char *bssid, diff --git a/src/supplicant/nm-supplicant-manager.c b/src/supplicant/nm-supplicant-manager.c index 5ab96f88..104aeee7 100644 --- a/src/supplicant/nm-supplicant-manager.c +++ b/src/supplicant/nm-supplicant-manager.c @@ -23,8 +23,6 @@ #include "nm-supplicant-manager.h" -#include <string.h> - #include "nm-supplicant-interface.h" #include "nm-supplicant-types.h" #include "nm-core-internal.h" @@ -41,6 +39,8 @@ typedef struct { NMSupplicantFeature ap_support; NMSupplicantFeature pmf_support; NMSupplicantFeature fils_support; + NMSupplicantFeature p2p_support; + NMSupplicantFeature wfd_support; guint die_count_reset_id; guint die_count; } NMSupplicantManagerPrivate; @@ -69,7 +69,7 @@ NM_CACHED_QUARK_FCN ("nm-supplicant-error-quark", nm_supplicant_error_quark) /*****************************************************************************/ -static inline gboolean +static gboolean die_count_exceeded (guint32 count) { return count > 2; @@ -123,6 +123,72 @@ _sup_iface_last_ref (gpointer data, g_object_remove_toggle_ref ((GObject *) sup_iface, _sup_iface_last_ref, self); } +static void +on_supplicant_wfd_ies_set (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + gs_unref_variant GVariant *result = NULL; + gs_free_error GError *error = NULL; + + result = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object), res, &error); + + if (!result) + _LOGW ("failed to set WFD IEs on wpa_supplicant: %s", error->message); +} + +/** + * nm_supplicant_manager_set_wfd_ies: + * @self: the #NMSupplicantManager + * @wfd_ies: a #GBytes with the WFD IEs or %NULL + * + * This function sets the global WFD IEs on wpa_supplicant. Note that + * it would make more sense if this was per-device, but wpa_supplicant + * simply does not work that way. + * */ +void +nm_supplicant_manager_set_wfd_ies (NMSupplicantManager *self, + GBytes *wfd_ies) +{ + NMSupplicantManagerPrivate *priv; + GVariantBuilder params; + GVariant *val; + + g_return_if_fail (NM_IS_SUPPLICANT_MANAGER (self)); + + priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self); + + _LOGD ("setting WFD IEs for P2P operation"); + + if (wfd_ies) + val = g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, + g_bytes_get_data (wfd_ies, NULL), + g_bytes_get_size (wfd_ies), + sizeof (guint8)); + else + val = g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, + NULL, 0, sizeof (guint8)); + + g_variant_builder_init (¶ms, G_VARIANT_TYPE ("(ssv)")); + + g_variant_builder_add (¶ms, "s", g_dbus_proxy_get_interface_name (priv->proxy)); + g_variant_builder_add (¶ms, "s", "WFDIEs"); + g_variant_builder_add_value (¶ms, g_variant_new_variant (val)); + + g_dbus_connection_call (g_dbus_proxy_get_connection (priv->proxy), + g_dbus_proxy_get_name (priv->proxy), + g_dbus_proxy_get_object_path (priv->proxy), + "org.freedesktop.DBus.Properties", + "Set", + g_variant_builder_end (¶ms), + G_VARIANT_TYPE_UNIT, + G_DBUS_CALL_FLAGS_NO_AUTO_START, + 1000, + NULL, + on_supplicant_wfd_ies_set, + NULL); +} + /** * nm_supplicant_manager_create_interface: * @self: the #NMSupplicantManager @@ -159,11 +225,71 @@ nm_supplicant_manager_create_interface (NMSupplicantManager *self, } iface = nm_supplicant_interface_new (ifname, + NULL, driver, priv->fast_support, priv->ap_support, priv->pmf_support, - priv->fils_support); + priv->fils_support, + priv->p2p_support, + priv->wfd_support); + + priv->ifaces = g_slist_prepend (priv->ifaces, iface); + g_object_add_toggle_ref ((GObject *) iface, _sup_iface_last_ref, self); + + /* If we're making the supplicant take a time out for a bit, don't + * let the supplicant interface start immediately, just let it hang + * around in INIT state until we're ready to talk to the supplicant + * again. + */ + if (is_available (self)) + nm_supplicant_interface_set_supplicant_available (iface, TRUE); + + return iface; +} + +/** + * nm_supplicant_manager_create_interface_from_path: + * @self: the #NMSupplicantManager + * @object_path: the DBus object path for which to obtain the supplicant interface + * + * Note: the manager owns a reference to the instance and the only way to + * get the manager to release it, is by dropping all other references + * to the supplicant-interface (or destroying the manager). + * + * Returns: (transfer full): returns a #NMSupplicantInterface or %NULL. + * Must be unrefed at the end. + * */ +NMSupplicantInterface * +nm_supplicant_manager_create_interface_from_path (NMSupplicantManager *self, + const char *object_path) +{ + NMSupplicantManagerPrivate *priv; + NMSupplicantInterface *iface; + GSList *ifaces; + + g_return_val_if_fail (NM_IS_SUPPLICANT_MANAGER (self), NULL); + g_return_val_if_fail (object_path != NULL, NULL); + + priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self); + + _LOGD ("creating new supplicant interface for dbus path %s", object_path); + + /* assert against not requesting duplicate interfaces. */ + for (ifaces = priv->ifaces; ifaces; ifaces = ifaces->next) { + if (g_strcmp0 (nm_supplicant_interface_get_object_path (ifaces->data), object_path) == 0) + g_return_val_if_reached (NULL); + } + + iface = nm_supplicant_interface_new (NULL, + object_path, + NM_SUPPLICANT_DRIVER_WIRELESS, + priv->fast_support, + priv->ap_support, + priv->pmf_support, + priv->fils_support, + priv->p2p_support, + priv->wfd_support); priv->ifaces = g_slist_prepend (priv->ifaces, iface); g_object_add_toggle_ref ((GObject *) iface, _sup_iface_last_ref, self); @@ -199,6 +325,8 @@ update_capabilities (NMSupplicantManager *self) priv->ap_support = NM_SUPPLICANT_FEATURE_UNKNOWN; priv->pmf_support = NM_SUPPLICANT_FEATURE_UNKNOWN; priv->fils_support = NM_SUPPLICANT_FEATURE_UNKNOWN; + /* P2P support is newer than the capabilities property */ + priv->p2p_support = NM_SUPPLICANT_FEATURE_NO; value = g_dbus_proxy_get_cached_property (priv->proxy, "Capabilities"); if (value) { @@ -207,6 +335,7 @@ update_capabilities (NMSupplicantManager *self) priv->ap_support = NM_SUPPLICANT_FEATURE_NO; priv->pmf_support = NM_SUPPLICANT_FEATURE_NO; priv->fils_support = NM_SUPPLICANT_FEATURE_NO; + priv->p2p_support = NM_SUPPLICANT_FEATURE_NO; if (array) { if (g_strv_contains (array, "ap")) priv->ap_support = NM_SUPPLICANT_FEATURE_YES; @@ -214,17 +343,20 @@ update_capabilities (NMSupplicantManager *self) priv->pmf_support = NM_SUPPLICANT_FEATURE_YES; if (g_strv_contains (array, "fils")) priv->fils_support = NM_SUPPLICANT_FEATURE_YES; + if (g_strv_contains (array, "p2p")) + priv->p2p_support = NM_SUPPLICANT_FEATURE_YES; g_free (array); } } g_variant_unref (value); } - /* Tell all interfaces about results of the AP/PMF/FILS check */ + /* Tell all interfaces about results of the AP/PMF/FILS/P2P check */ for (ifaces = priv->ifaces; ifaces; ifaces = ifaces->next) { nm_supplicant_interface_set_ap_support (ifaces->data, priv->ap_support); nm_supplicant_interface_set_pmf_support (ifaces->data, priv->pmf_support); nm_supplicant_interface_set_fils_support (ifaces->data, priv->fils_support); + nm_supplicant_interface_set_p2p_support (ifaces->data, priv->p2p_support); } _LOGD ("AP mode is %ssupported", @@ -236,6 +368,9 @@ update_capabilities (NMSupplicantManager *self) _LOGD ("FILS is %ssupported", (priv->fils_support == NM_SUPPLICANT_FEATURE_YES) ? "" : (priv->fils_support == NM_SUPPLICANT_FEATURE_NO) ? "not " : "possibly "); + _LOGD ("P2P is %ssupported", + (priv->p2p_support == NM_SUPPLICANT_FEATURE_YES) ? "" : + (priv->p2p_support == NM_SUPPLICANT_FEATURE_NO) ? "not " : "possibly "); /* EAP-FAST */ priv->fast_support = NM_SUPPLICANT_FEATURE_NO; @@ -264,6 +399,20 @@ update_capabilities (NMSupplicantManager *self) _LOGD ("EAP-FAST is %ssupported", (priv->fast_support == NM_SUPPLICANT_FEATURE_YES) ? "" : (priv->fast_support == NM_SUPPLICANT_FEATURE_NO) ? "not " : "possibly "); + + priv->wfd_support = NM_SUPPLICANT_FEATURE_NO; + value = g_dbus_proxy_get_cached_property (priv->proxy, "WFDIEs"); + if (value) { + priv->wfd_support = NM_SUPPLICANT_FEATURE_YES; + g_variant_unref (value); + } + + for (ifaces = priv->ifaces; ifaces; ifaces = ifaces->next) + nm_supplicant_interface_set_wfd_support (ifaces->data, priv->fast_support); + + _LOGD ("WFD is %ssupported", + (priv->wfd_support == NM_SUPPLICANT_FEATURE_YES) ? "" : + (priv->wfd_support == NM_SUPPLICANT_FEATURE_NO) ? "not " : "possibly "); } static void diff --git a/src/supplicant/nm-supplicant-manager.h b/src/supplicant/nm-supplicant-manager.h index 8928cf20..058745fb 100644 --- a/src/supplicant/nm-supplicant-manager.h +++ b/src/supplicant/nm-supplicant-manager.h @@ -38,8 +38,13 @@ GType nm_supplicant_manager_get_type (void); NMSupplicantManager *nm_supplicant_manager_get (void); +void nm_supplicant_manager_set_wfd_ies (NMSupplicantManager *self, + GBytes *wfd_ies); + NMSupplicantInterface *nm_supplicant_manager_create_interface (NMSupplicantManager *mgr, const char *ifname, NMSupplicantDriver driver); +NMSupplicantInterface *nm_supplicant_manager_create_interface_from_path (NMSupplicantManager *self, + const char *object_path); #endif /* __NETWORKMANAGER_SUPPLICANT_MANAGER_H__ */ diff --git a/src/supplicant/nm-supplicant-settings-verify.c b/src/supplicant/nm-supplicant-settings-verify.c index 1e25675d..f10bbb04 100644 --- a/src/supplicant/nm-supplicant-settings-verify.c +++ b/src/supplicant/nm-supplicant-settings-verify.c @@ -24,8 +24,6 @@ #include <stdio.h> #include <stdlib.h> -#include <string.h> -#include <errno.h> struct Opt { const char * key; @@ -72,7 +70,7 @@ const char * proto_allowed[] = { "WPA", "RSN", NULL }; const char * key_mgmt_allowed[] = { "WPA-PSK", "WPA-PSK-SHA256", "WPA-EAP", "WPA-EAP-SHA256", "FILS-SHA256", "FILS-SHA384", - "IEEE8021X", "WPA-NONE", + "IEEE8021X", "WPA-NONE", "SAE", "NONE", NULL }; const char * auth_alg_allowed[] = { "OPEN", "SHARED", "LEAP", NULL }; const char * eap_allowed[] = { "LEAP", "MD5", "TLS", "PEAP", "TTLS", "SIM", @@ -159,23 +157,13 @@ validate_type_int (const struct Opt * opt, const char * value, const guint32 len) { - long int intval; + gint64 v; g_return_val_if_fail (opt != NULL, FALSE); g_return_val_if_fail (value != NULL, FALSE); - errno = 0; - intval = strtol (value, NULL, 10); - if (errno != 0) - return FALSE; - - /* strtol returns a long, but we are dealing with ints */ - if (intval > INT_MAX || intval < INT_MIN) - return FALSE; - if (intval > opt->int_high || intval < opt->int_low) - return FALSE; - - return TRUE; + v = _nm_utils_ascii_str_to_int64 (value, 10, opt->int_low, opt->int_high, G_MININT64); + return v != G_MININT64 || errno == 0; } static gboolean diff --git a/src/supplicant/tests/meson.build b/src/supplicant/tests/meson.build index 5e4cbdbe..fbccb313 100644 --- a/src/supplicant/tests/meson.build +++ b/src/supplicant/tests/meson.build @@ -9,5 +9,6 @@ exe = executable( test( 'supplicant/' + test_unit, test_script, - args: test_args + [exe.full_path()] + args: test_args + [exe.full_path()], + timeout: default_test_timeout, ) diff --git a/src/supplicant/tests/test-supplicant-config.c b/src/supplicant/tests/test-supplicant-config.c index d7ec1fe2..2c7a71a3 100644 --- a/src/supplicant/tests/test-supplicant-config.c +++ b/src/supplicant/tests/test-supplicant-config.c @@ -23,7 +23,6 @@ #include <stdio.h> #include <stdarg.h> #include <unistd.h> -#include <string.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/socket.h> @@ -389,6 +388,76 @@ test_wifi_wpa_psk (const char *detail, } static void +test_wifi_sae_psk (const char *psk) +{ + gs_unref_object NMConnection *connection = NULL; + gs_unref_variant GVariant *config_dict = NULL; + NMSettingWirelessSecurity *s_wsec; + gboolean success; + GError *error = NULL; + const unsigned char ssid_data[] = { 0x54, 0x65, 0x73, 0x74, 0x20, 0x53, 0x53, 0x49, 0x44 }; + gs_unref_bytes GBytes *ssid = g_bytes_new (ssid_data, sizeof (ssid_data)); + const char *bssid_str = "11:22:33:44:55:66"; + int short_psk = strlen (psk) < 8; + + connection = new_basic_connection ("Test Wifi SAE", ssid, bssid_str); + + /* Wifi Security setting */ + s_wsec = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new (); + nm_connection_add_setting (connection, NM_SETTING (s_wsec)); + g_object_set (s_wsec, + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "sae", + NM_SETTING_WIRELESS_SECURITY_PSK, psk, + NULL); + nm_setting_wireless_security_add_proto (s_wsec, "rsn"); + nm_setting_wireless_security_add_pairwise (s_wsec, "tkip"); + nm_setting_wireless_security_add_pairwise (s_wsec, "ccmp"); + nm_setting_wireless_security_add_group (s_wsec, "tkip"); + nm_setting_wireless_security_add_group (s_wsec, "ccmp"); + + success = nm_connection_verify (connection, &error); + g_assert_no_error (error); + g_assert (success); + + NMTST_EXPECT_NM_INFO ("Config: added 'ssid' value 'Test SSID'*"); + NMTST_EXPECT_NM_INFO ("Config: added 'scan_ssid' value '1'*"); + NMTST_EXPECT_NM_INFO ("Config: added 'bssid' value '11:22:33:44:55:66'*"); + NMTST_EXPECT_NM_INFO ("Config: added 'freq_list' value *"); + NMTST_EXPECT_NM_INFO ("Config: added 'key_mgmt' value 'SAE'"); + if (short_psk) + NMTST_EXPECT_NM_INFO ("Config: added 'sae_password' value *"); + else + NMTST_EXPECT_NM_INFO ("Config: added 'psk' value *"); + NMTST_EXPECT_NM_INFO ("Config: added 'proto' value 'RSN'"); + NMTST_EXPECT_NM_INFO ("Config: added 'pairwise' value 'TKIP CCMP'"); + NMTST_EXPECT_NM_INFO ("Config: added 'group' value 'TKIP CCMP'"); + NMTST_EXPECT_NM_INFO ("Config: added 'ieee80211w' value '0'"); + config_dict = build_supplicant_config (connection, 1500, 0, TRUE, TRUE); + + g_test_assert_expected_messages (); + g_assert (config_dict); + + validate_opt ("wifi-sae", config_dict, "scan_ssid", TYPE_INT, GINT_TO_POINTER (1)); + validate_opt ("wifi-sae", config_dict, "ssid", TYPE_BYTES, ssid); + validate_opt ("wifi-sae", config_dict, "bssid", TYPE_KEYWORD, bssid_str); + validate_opt ("wifi-sae", config_dict, "key_mgmt", TYPE_KEYWORD, "SAE"); + validate_opt ("wifi-sae", config_dict, "proto", TYPE_KEYWORD, "RSN"); + validate_opt ("wifi-sae", config_dict, "pairwise", TYPE_KEYWORD, "TKIP CCMP"); + validate_opt ("wifi-sae", config_dict, "group", TYPE_KEYWORD, "TKIP CCMP"); + if (short_psk) + validate_opt ("wifi-sae", config_dict, "sae_password", TYPE_KEYWORD, psk); + else + validate_opt ("wifi-sae", config_dict, "psk", TYPE_KEYWORD, psk); +} + +static void +test_wifi_sae (void) +{ + test_wifi_sae_psk ("Moo"); + test_wifi_sae_psk ("Hello World!"); +} + +static void test_wifi_wpa_psk_types (void) { const char *key1 = "d4721e911461d3cdef9793858e977fcda091779243abb7316c2f11605a160893"; @@ -580,6 +649,7 @@ int main (int argc, char **argv) g_test_add_func ("/supplicant-config/wifi-eap/locked-bssid", test_wifi_eap_locked_bssid); g_test_add_func ("/supplicant-config/wifi-eap/unlocked-bssid", test_wifi_eap_unlocked_bssid); g_test_add_func ("/supplicant-config/wifi-eap/fils-disabled", test_wifi_eap_fils_disabled); + g_test_add_func ("/supplicant-config/wifi-sae", test_wifi_sae); return g_test_run (); } |