#include <string.h>
#include <stdint.h>
#include <syslog.h>
+#include <ctype.h>
#include <glib.h>
#include <gdbus.h>
static GHashTable *interface_table;
static GHashTable *bss_mapping;
+struct _GSupplicantWpsCredentials {
+ unsigned char ssid[32];
+ unsigned int ssid_len;
+ char *key;
+};
+
struct _GSupplicantInterface {
char *path;
char *network_path;
unsigned int pairwise_capa;
unsigned int scan_capa;
unsigned int mode_capa;
+ unsigned int max_scan_ssids;
dbus_bool_t ready;
GSupplicantState state;
dbus_bool_t scanning;
char *ifname;
char *driver;
char *bridge;
+ struct _GSupplicantWpsCredentials wps_cred;
+ GSupplicantWpsState wps_state;
GHashTable *network_table;
GHashTable *net_mapping;
GHashTable *bss_mapping;
void *data;
};
-struct _GSupplicantNetwork {
- GSupplicantInterface *interface;
- char *path;
- char *group;
- char *name;
- unsigned char ssid[32];
- unsigned int ssid_len;
- dbus_int16_t signal;
- GSupplicantMode mode;
- GSupplicantSecurity security;
- GHashTable *bss_table;
- GHashTable *config_table;
-};
-
struct g_supplicant_bss {
GSupplicantInterface *interface;
char *path;
dbus_bool_t privacy;
dbus_bool_t psk;
dbus_bool_t ieee8021x;
+ unsigned int wps_capabilities;
+};
+
+struct _GSupplicantNetwork {
+ GSupplicantInterface *interface;
+ char *path;
+ char *group;
+ char *name;
+ unsigned char ssid[32];
+ unsigned int ssid_len;
+ dbus_int16_t signal;
+ dbus_uint16_t frequency;
+ struct g_supplicant_bss *best_bss;
+ GSupplicantMode mode;
+ GSupplicantSecurity security;
+ dbus_bool_t wps;
+ unsigned int wps_capabilities;
+ GHashTable *bss_table;
+ GHashTable *config_table;
};
static inline void debug(const char *format, ...)
return "managed";
case G_SUPPLICANT_MODE_IBSS:
return "adhoc";
+ case G_SUPPLICANT_MODE_MASTER:
+ return "ap";
}
return NULL;
callbacks_pointer->network_removed(network);
}
+static void callback_network_changed(GSupplicantNetwork *network,
+ const char *property)
+{
+ if (callbacks_pointer == NULL)
+ return;
+
+ if (callbacks_pointer->network_changed == NULL)
+ return;
+
+ callbacks_pointer->network_changed(network, property);
+}
+
static void remove_interface(gpointer data)
{
GSupplicantInterface *interface = data;
callback_interface_removed(interface);
+ g_free(interface->wps_cred.key);
g_free(interface->path);
g_free(interface->network_path);
g_free(interface->ifname);
else if (g_strcmp0(key, "Modes") == 0)
supplicant_dbus_array_foreach(iter,
interface_capability_mode, interface);
- else
+ else if (g_strcmp0(key, "MaxScanSSID") == 0) {
+ dbus_int32_t max_scan_ssid;
+
+ dbus_message_iter_get_basic(iter, &max_scan_ssid);
+ interface->max_scan_ssids = max_scan_ssid;
+
+ } else
SUPPLICANT_DBG("key %s type %c",
key, dbus_message_iter_get_arg_type(iter));
}
+static void set_apscan(DBusMessageIter *iter, void *user_data)
+{
+ unsigned int ap_scan = *(unsigned int *)user_data;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32, &ap_scan);
+}
+
+int g_supplicant_interface_set_apscan(GSupplicantInterface *interface,
+ unsigned int ap_scan)
+{
+ return supplicant_dbus_property_set(interface->path,
+ SUPPLICANT_INTERFACE ".Interface",
+ "ApScan", DBUS_TYPE_UINT32_AS_STRING,
+ set_apscan, NULL, &ap_scan);
+}
+
void g_supplicant_interface_set_data(GSupplicantInterface *interface,
void *data)
{
return interface->state;
}
+const char *g_supplicant_interface_get_wps_key(GSupplicantInterface *interface)
+{
+ if (interface == NULL)
+ return NULL;
+
+ return (const char *)interface->wps_cred.key;
+}
+
+const void *g_supplicant_interface_get_wps_ssid(GSupplicantInterface *interface,
+ unsigned int *ssid_len)
+{
+ if (ssid_len == NULL)
+ return NULL;
+
+ if (interface == NULL || interface->wps_cred.ssid == NULL) {
+ *ssid_len = 0;
+ return NULL;
+ }
+
+ *ssid_len = interface->wps_cred.ssid_len;
+ return interface->wps_cred.ssid;
+}
+
+GSupplicantWpsState g_supplicant_interface_get_wps_state(
+ GSupplicantInterface *interface)
+{
+ if (interface == NULL)
+ return G_SUPPLICANT_WPS_STATE_UNKNOWN;
+
+ return interface->wps_state;
+}
+
+unsigned int g_supplicant_interface_get_mode(GSupplicantInterface *interface)
+{
+ if (interface == NULL)
+ return 0;
+
+ return interface->mode_capa;
+}
+
+unsigned int g_supplicant_interface_get_max_scan_ssids(
+ GSupplicantInterface *interface)
+{
+ if (interface == NULL)
+ return 0;
+
+ return interface->max_scan_ssids;
+}
+
+static void set_network_enabled(DBusMessageIter *iter, void *user_data)
+{
+ dbus_bool_t enable = *(dbus_bool_t *)user_data;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &enable);
+}
+
+int g_supplicant_interface_enable_selected_network(GSupplicantInterface *interface,
+ dbus_bool_t enable)
+{
+ if (interface == NULL)
+ return -1;
+
+ if (interface->network_path == NULL)
+ return -1;
+
+ SUPPLICANT_DBG(" ");
+ return supplicant_dbus_property_set(interface->network_path,
+ SUPPLICANT_INTERFACE ".Network",
+ "Enabled", DBUS_TYPE_BOOLEAN_AS_STRING,
+ set_network_enabled, NULL, &enable);
+}
+
+dbus_bool_t g_supplicant_interface_get_ready(GSupplicantInterface *interface)
+{
+ if (interface == NULL)
+ return FALSE;
+
+ return interface->ready;
+}
+
GSupplicantInterface *g_supplicant_network_get_interface(
GSupplicantNetwork *network)
{
return network->signal;
}
+dbus_uint16_t g_supplicant_network_get_frequency(GSupplicantNetwork *network)
+{
+ if (network == NULL)
+ return 0;
+
+ return network->frequency;
+}
+
+dbus_bool_t g_supplicant_network_get_wps(GSupplicantNetwork *network)
+{
+ if (network == NULL)
+ return FALSE;
+
+ return network->wps;
+}
+
+dbus_bool_t g_supplicant_network_is_wps_active(GSupplicantNetwork *network)
+{
+ if (network == NULL)
+ return FALSE;
+
+ if (network->wps_capabilities & G_SUPPLICANT_WPS_CONFIGURED)
+ return TRUE;
+
+ return FALSE;
+}
+
+dbus_bool_t g_supplicant_network_is_wps_pbc(GSupplicantNetwork *network)
+{
+ if (network == NULL)
+ return FALSE;
+
+ if (network->wps_capabilities & G_SUPPLICANT_WPS_PBC)
+ return TRUE;
+
+ return FALSE;
+}
+
+dbus_bool_t g_supplicant_network_is_wps_advertizing(GSupplicantNetwork *network)
+{
+ if (network == NULL)
+ return FALSE;
+
+ if (network->wps_capabilities & G_SUPPLICANT_WPS_REGISTRAR)
+ return TRUE;
+
+ return FALSE;
+}
+
static void merge_network(GSupplicantNetwork *network)
{
GString *str;
static char *create_name(unsigned char *ssid, int ssid_len)
{
- char *name;
- int i;
+ GString *string;
+ const gchar *remainder, *invalid;
+ int valid_bytes, remaining_bytes;
if (ssid_len < 1 || ssid[0] == '\0')
- name = NULL;
- else
- name = g_try_malloc0(ssid_len + 1);
-
- if (name == NULL)
return g_strdup("");
- for (i = 0; i < ssid_len; i++) {
- if (g_ascii_isprint(ssid[i]))
- name[i] = ssid[i];
- else
- name[i] = ' ';
+ string = NULL;
+ remainder = (const gchar *)ssid;
+ remaining_bytes = ssid_len;
+
+ while (remaining_bytes != 0) {
+ if (g_utf8_validate(remainder, remaining_bytes,
+ &invalid) == TRUE) {
+ break;
+ }
+
+ valid_bytes = invalid - remainder;
+
+ if (string == NULL)
+ string = g_string_sized_new(remaining_bytes);
+
+ g_string_append_len(string, remainder, valid_bytes);
+
+ /* append U+FFFD REPLACEMENT CHARACTER */
+ g_string_append(string, "\357\277\275");
+
+ remaining_bytes -= valid_bytes + 1;
+ remainder = invalid + 1;
}
- return name;
+ if (string == NULL)
+ return g_strndup((const gchar *)ssid, ssid_len + 1);
+
+ g_string_append(string, remainder);
+
+ return g_string_free(string, FALSE);
}
static char *create_group(struct g_supplicant_bss *bss)
return g_string_free(str, FALSE);
}
-static void add_bss_to_network(struct g_supplicant_bss *bss)
+static void add_or_replace_bss_to_network(struct g_supplicant_bss *bss)
{
GSupplicantInterface *interface = bss->interface;
GSupplicantNetwork *network;
network->ssid_len = bss->ssid_len;
memcpy(network->ssid, bss->ssid, bss->ssid_len);
network->signal = bss->signal;
+ network->frequency = bss->frequency;
+ network->best_bss = bss;
+
+ network->wps = FALSE;
+ if ((bss->keymgmt & G_SUPPLICANT_KEYMGMT_WPS) != 0) {
+ network->wps = TRUE;
+
+ network->wps_capabilities |= bss->wps_capabilities;
+ }
network->bss_table = g_hash_table_new_full(g_str_hash, g_str_equal,
NULL, remove_bss);
callback_network_added(network);
done:
+ if (bss->signal > network->signal) {
+ network->signal = bss->signal;
+ network->best_bss = bss;
+ callback_network_changed(network, "Signal");
+ }
+
g_hash_table_replace(interface->bss_mapping, bss->path, network);
g_hash_table_replace(network->bss_table, bss->path, bss);
}
+static unsigned int get_tlv(unsigned char *ie, unsigned int ie_size,
+ unsigned int type)
+{
+ unsigned int len = 0;
+
+ while (len + 4 < ie_size) {
+ unsigned int hi = ie[len];
+ unsigned int lo = ie[len + 1];
+ unsigned int tmp_type = (hi << 8) + lo;
+ unsigned int v_len = 0;
+
+ /* hi and lo are used to recreate an unsigned int
+ * based on 2 8bits length unsigned int. */
+
+ hi = ie[len + 2];
+ lo = ie[len + 3];
+ v_len = (hi << 8) + lo;
+
+ if (tmp_type == type) {
+ unsigned int ret_value = 0;
+ unsigned char *value = (unsigned char *)&ret_value;
+
+ SUPPLICANT_DBG("IE: match type 0x%x", type);
+
+ /* Verifying length relevance */
+ if (v_len > sizeof(unsigned int) ||
+ len + 4 + v_len > ie_size)
+ break;
+
+ memcpy(value, ie + len + 4, v_len);
+
+ SUPPLICANT_DBG("returning 0x%x", ret_value);
+ return ret_value;
+ }
+
+ len += v_len + 4;
+ }
+
+ SUPPLICANT_DBG("returning 0");
+ return 0;
+}
+
+static void bss_process_ies(DBusMessageIter *iter, void *user_data)
+{
+ struct g_supplicant_bss *bss = user_data;
+ const unsigned char WPS_OUI[] = { 0x00, 0x50, 0xf2, 0x04 };
+ unsigned char *ie, *ie_end;
+ DBusMessageIter array;
+ unsigned int value;
+ int ie_len;
+
+#define WMM_WPA1_WPS_INFO 221
+#define WPS_INFO_MIN_LEN 6
+#define WPS_VERSION_TLV 0x104A
+#define WPS_STATE_TLV 0x1044
+#define WPS_METHODS_TLV 0x1012
+#define WPS_REGISTRAR_TLV 0x1041
+#define WPS_VERSION 0x10
+#define WPS_PBC 0x04
+#define WPS_PIN 0x00
+#define WPS_CONFIGURED 0x02
+
+ dbus_message_iter_recurse(iter, &array);
+ dbus_message_iter_get_fixed_array(&array, &ie, &ie_len);
+
+ if (ie == NULL || ie_len < 2)
+ return;
+
+ for (ie_end = ie + ie_len; ie < ie_end && ie + ie[1] + 1 <= ie_end;
+ ie += ie[1] + 2) {
+
+ if (ie[0] != WMM_WPA1_WPS_INFO || ie[1] < WPS_INFO_MIN_LEN ||
+ memcmp(ie+2, WPS_OUI, sizeof(WPS_OUI)) != 0)
+ continue;
+
+ SUPPLICANT_DBG("IE: match WPS_OUI");
+
+ value = get_tlv(&ie[6], ie[1], WPS_STATE_TLV);
+ if (get_tlv(&ie[6], ie[1], WPS_VERSION_TLV) == WPS_VERSION &&
+ value != 0) {
+ bss->keymgmt |= G_SUPPLICANT_KEYMGMT_WPS;
+
+ if (value == WPS_CONFIGURED)
+ bss->wps_capabilities |=
+ G_SUPPLICANT_WPS_CONFIGURED;
+ }
+
+ value = get_tlv(&ie[6], ie[1], WPS_METHODS_TLV);
+ if (value != 0) {
+ if (GUINT16_FROM_BE(value) == WPS_PBC)
+ bss->wps_capabilities |= G_SUPPLICANT_WPS_PBC;
+ if (GUINT16_FROM_BE(value) == WPS_PIN)
+ bss->wps_capabilities |= G_SUPPLICANT_WPS_PIN;
+ } else
+ bss->wps_capabilities |=
+ G_SUPPLICANT_WPS_PBC | G_SUPPLICANT_WPS_PIN;
+
+ /* If the AP sends this it means it's advertizing
+ * as a registrar and the WPS process is launched
+ * on its side */
+ if (get_tlv(&ie[6], ie[1], WPS_REGISTRAR_TLV) != 0)
+ bss->wps_capabilities |= G_SUPPLICANT_WPS_REGISTRAR;
+
+ SUPPLICANT_DBG("WPS Methods 0x%x", bss->wps_capabilities);
+ }
+}
+
+static void bss_compute_security(struct g_supplicant_bss *bss)
+{
+ if (bss->ieee8021x == TRUE)
+ bss->security = G_SUPPLICANT_SECURITY_IEEE8021X;
+ else if (bss->psk == TRUE)
+ bss->security = G_SUPPLICANT_SECURITY_PSK;
+ else if (bss->privacy == TRUE)
+ bss->security = G_SUPPLICANT_SECURITY_WEP;
+ else
+ bss->security = G_SUPPLICANT_SECURITY_NONE;
+}
+
static void bss_property(const char *key, DBusMessageIter *iter,
void *user_data)
SUPPLICANT_DBG("key %s", key);
if (key == NULL) {
- if (bss->ieee8021x == TRUE)
- bss->security = G_SUPPLICANT_SECURITY_IEEE8021X;
- else if (bss->psk == TRUE)
- bss->security = G_SUPPLICANT_SECURITY_PSK;
- else if (bss->privacy == TRUE)
- bss->security = G_SUPPLICANT_SECURITY_WEP;
- else
- bss->security = G_SUPPLICANT_SECURITY_NONE;
+ bss_compute_security(bss);
- add_bss_to_network(bss);
+ add_or_replace_bss_to_network(bss);
return;
}
G_SUPPLICANT_KEYMGMT_WPA_FT_PSK |
G_SUPPLICANT_KEYMGMT_WPA_PSK_256))
bss->psk = TRUE;
- } else
+ } else if (g_strcmp0(key, "IEs") == 0)
+ bss_process_ies(iter, bss);
+ else
SUPPLICANT_DBG("key %s type %c",
key, dbus_message_iter_get_arg_type(iter));
}
bss_property, bss);
}
+static void update_signal(gpointer key, gpointer value,
+ gpointer user_data)
+{
+ struct g_supplicant_bss *bss = value;
+ GSupplicantNetwork *network = user_data;
+
+ if (bss->signal > network->signal) {
+ network->signal = bss->signal;
+ network->best_bss = bss;
+ }
+}
+
+static void update_network_signal(GSupplicantNetwork *network)
+{
+ if (g_hash_table_size(network->bss_table) <= 1)
+ return;
+
+ g_hash_table_foreach(network->bss_table,
+ update_signal, network);
+
+ SUPPLICANT_DBG("New network signal %d", network->signal);
+}
+
static void interface_bss_removed(DBusMessageIter *iter, void *user_data)
{
GSupplicantInterface *interface = user_data;
g_hash_table_remove(interface->bss_mapping, path);
g_hash_table_remove(network->bss_table, path);
+ update_network_signal(network);
+
if (g_hash_table_size(network->bss_table) == 0)
g_hash_table_remove(interface->network_table, network->group);
}
return;
supplicant_dbus_property_foreach(iter, bss_property, bss);
+
+ if (bss->signal == network->signal)
+ return;
+
+ /*
+ * If the new signal is lower than the SSID signal, we need
+ * to check for the new maximum.
+ */
+ if (bss->signal < network->signal) {
+ if (bss != network->best_bss)
+ return;
+ network->signal = bss->signal;
+ update_network_signal(network);
+ } else {
+ network->signal = bss->signal;
+ network->best_bss = bss;
+ }
+
+ SUPPLICANT_DBG("New network signal for %s %d dBm", network->ssid, network->signal);
+
+ callback_network_changed(network, "Signal");
+}
+
+static void wps_credentials(const char *key, DBusMessageIter *iter,
+ void *user_data)
+{
+ GSupplicantInterface *interface = user_data;
+
+ if (key == NULL)
+ return;
+
+ SUPPLICANT_DBG("key %s", key);
+
+ if (g_strcmp0(key, "Key") == 0) {
+ DBusMessageIter array;
+ unsigned char *key;
+ int key_len;
+
+ dbus_message_iter_recurse(iter, &array);
+ dbus_message_iter_get_fixed_array(&array, &key, &key_len);
+
+ g_free(interface->wps_cred.key);
+ interface->wps_cred.key = g_try_malloc0(
+ sizeof(char) * key_len+1);
+
+ if (interface->wps_cred.key == NULL)
+ return;
+
+ memcpy(interface->wps_cred.key, key, sizeof(char) * key_len);
+
+ SUPPLICANT_DBG("WPS key present");
+ } else if (g_strcmp0(key, "SSID") == 0) {
+ DBusMessageIter array;
+ unsigned char *ssid;
+ int ssid_len;
+
+ dbus_message_iter_recurse(iter, &array);
+ dbus_message_iter_get_fixed_array(&array, &ssid, &ssid_len);
+
+ if (ssid_len > 0 && ssid_len < 33) {
+ memcpy(interface->wps_cred.ssid, ssid, ssid_len);
+ interface->wps_cred.ssid_len = ssid_len;
+ } else {
+ memset(interface->wps_cred.ssid, 0, 32);
+ interface->wps_cred.ssid_len = 0;
+ }
+ }
+}
+
+static void signal_wps_credentials(const char *path, DBusMessageIter *iter)
+{
+ GSupplicantInterface *interface;
+
+ SUPPLICANT_DBG("");
+
+ interface = g_hash_table_lookup(interface_table, path);
+ if (interface == NULL)
+ return;
+
+ supplicant_dbus_property_foreach(iter, wps_credentials, interface);
+}
+
+static void wps_event_args(const char *key, DBusMessageIter *iter,
+ void *user_data)
+{
+ GSupplicantInterface *interface = user_data;
+
+ if (key == NULL || interface == NULL)
+ return;
+
+ SUPPLICANT_DBG("Arg Key %s", key);
+}
+
+static void signal_wps_event(const char *path, DBusMessageIter *iter)
+{
+ GSupplicantInterface *interface;
+ const char *name = NULL;
+
+ SUPPLICANT_DBG("");
+
+ interface = g_hash_table_lookup(interface_table, path);
+ if (interface == NULL)
+ return;
+
+ dbus_message_iter_get_basic(iter, &name);
+
+ SUPPLICANT_DBG("Name: %s", name);
+
+ if (g_strcmp0(name, "success") == 0)
+ interface->wps_state = G_SUPPLICANT_WPS_STATE_SUCCESS;
+ else if (g_strcmp0(name, "failed") == 0)
+ interface->wps_state = G_SUPPLICANT_WPS_STATE_FAIL;
+ else
+ interface->wps_state = G_SUPPLICANT_WPS_STATE_UNKNOWN;
+
+ if (!dbus_message_iter_has_next(iter))
+ return;
+
+ dbus_message_iter_next(iter);
+
+ supplicant_dbus_property_foreach(iter, wps_event_args, interface);
}
static struct {
{ SUPPLICANT_INTERFACE ".BSS", "PropertiesChanged", signal_bss_changed },
+ { SUPPLICANT_INTERFACE ".Interface.WPS", "Credentials", signal_wps_credentials },
+ { SUPPLICANT_INTERFACE ".Interface.WPS", "Event", signal_wps_event },
+
{ }
};
struct interface_create_data {
const char *ifname;
const char *driver;
+ const char *bridge;
GSupplicantInterface *interface;
GSupplicantInterfaceCallback callback;
void *user_data;
void *user_data;
};
+struct interface_scan_data {
+ GSupplicantInterface *interface;
+ GSupplicantInterfaceCallback callback;
+ GSupplicantScanParams *scan_params;
+ void *user_data;
+};
+
static void interface_create_property(const char *key, DBusMessageIter *iter,
void *user_data)
{
supplicant_dbus_dict_append_basic(&dict, "Driver",
DBUS_TYPE_STRING, &data->driver);
+ if (data->bridge != NULL)
+ supplicant_dbus_dict_append_basic(&dict, "BridgeIfname",
+ DBUS_TYPE_STRING, &data->bridge);
+
supplicant_dbus_dict_close(iter, &dict);
}
}
int g_supplicant_interface_create(const char *ifname, const char *driver,
+ const char *bridge,
GSupplicantInterfaceCallback callback,
void *user_data)
{
data->ifname = ifname;
data->driver = driver;
+ data->bridge = bridge;
data->callback = callback;
data->user_data = user_data;
static void interface_scan_result(const char *error,
DBusMessageIter *iter, void *user_data)
{
- struct interface_data *data = user_data;
+ struct interface_scan_data *data = user_data;
if (error != NULL) {
+ SUPPLICANT_DBG("error %s", error);
+
if (data->callback != NULL)
data->callback(-EIO, data->interface, data->user_data);
} else {
data->interface->scan_data = data->user_data;
}
+ if (data != NULL && data->scan_params != NULL)
+ g_free(data->scan_params);
+
dbus_free(data);
}
+static void add_scan_frequency(DBusMessageIter *iter, unsigned int freq)
+{
+ DBusMessageIter data;
+ unsigned int width = 0; /* Not used by wpa_supplicant atm */
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_STRUCT, NULL, &data);
+
+ dbus_message_iter_append_basic(&data, DBUS_TYPE_UINT32, &freq);
+ dbus_message_iter_append_basic(&data, DBUS_TYPE_UINT32, &width);
+
+ dbus_message_iter_close_container(iter, &data);
+}
+
+static void add_scan_frequencies(DBusMessageIter *iter,
+ void *user_data)
+{
+ GSupplicantScanParams *scan_data = user_data;
+ unsigned int freq;
+ int i;
+
+ for (i = 0; i < G_SUPPLICANT_MAX_FAST_SCAN; i++) {
+ freq = scan_data->freqs[i];
+ if (!freq)
+ break;
+
+ add_scan_frequency(iter, freq);
+ }
+}
+
+static void append_ssid(DBusMessageIter *iter,
+ const void *ssid, unsigned int len)
+{
+ DBusMessageIter array;
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+ DBUS_TYPE_BYTE_AS_STRING, &array);
+
+ dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
+ &ssid, len);
+ dbus_message_iter_close_container(iter, &array);
+}
+
+static void append_ssids(DBusMessageIter *iter, void *user_data)
+{
+ GSupplicantScanParams *scan_data = user_data;
+ int i;
+
+ for (i = 0; i < scan_data->num_ssids; i++)
+ append_ssid(iter, scan_data->ssids[i].ssid,
+ scan_data->ssids[i].ssid_len);
+}
+
+static void supplicant_add_scan_frequency(DBusMessageIter *dict,
+ supplicant_dbus_array_function function,
+ void *user_data)
+{
+ GSupplicantScanParams *scan_params = user_data;
+ DBusMessageIter entry, value, array;
+ const char *key = "Channels";
+
+ if (scan_params->freqs[0] != 0) {
+ dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
+ NULL, &entry);
+
+ dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
+
+ dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
+ DBUS_TYPE_ARRAY_AS_STRING
+ DBUS_STRUCT_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_UINT32_AS_STRING
+ DBUS_TYPE_UINT32_AS_STRING
+ DBUS_STRUCT_END_CHAR_AS_STRING,
+ &value);
+
+ dbus_message_iter_open_container(&value, DBUS_TYPE_ARRAY,
+ DBUS_STRUCT_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_UINT32_AS_STRING
+ DBUS_TYPE_UINT32_AS_STRING
+ DBUS_STRUCT_END_CHAR_AS_STRING,
+ &array);
+
+ if (function)
+ function(&array, user_data);
+
+ dbus_message_iter_close_container(&value, &array);
+ dbus_message_iter_close_container(&entry, &value);
+ dbus_message_iter_close_container(dict, &entry);
+ }
+}
+
static void interface_scan_params(DBusMessageIter *iter, void *user_data)
{
DBusMessageIter dict;
const char *type = "passive";
+ struct interface_scan_data *data = user_data;
supplicant_dbus_dict_open(iter, &dict);
- supplicant_dbus_dict_append_basic(&dict, "Type",
- DBUS_TYPE_STRING, &type);
+ if (data && data->scan_params) {
+ type = "active";
+
+ supplicant_dbus_dict_append_basic(&dict, "Type",
+ DBUS_TYPE_STRING, &type);
+
+ supplicant_dbus_dict_append_array(&dict, "SSIDs",
+ DBUS_TYPE_STRING,
+ append_ssids,
+ data->scan_params);
+
+ supplicant_add_scan_frequency(&dict, add_scan_frequencies,
+ data->scan_params);
+ } else
+ supplicant_dbus_dict_append_basic(&dict, "Type",
+ DBUS_TYPE_STRING, &type);
supplicant_dbus_dict_close(iter, &dict);
}
int g_supplicant_interface_scan(GSupplicantInterface *interface,
+ GSupplicantScanParams *scan_data,
GSupplicantInterfaceCallback callback,
void *user_data)
{
- struct interface_data *data;
+ struct interface_scan_data *data;
+ int ret;
if (interface == NULL)
return -EINVAL;
data->interface = interface;
data->callback = callback;
data->user_data = user_data;
+ data->scan_params = scan_data;
- return supplicant_dbus_method_call(interface->path,
+ ret = supplicant_dbus_method_call(interface->path,
SUPPLICANT_INTERFACE ".Interface", "Scan",
interface_scan_params, interface_scan_result, data);
+
+ if (ret < 0)
+ dbus_free(data);
+
+ return ret;
+}
+
+static int parse_supplicant_error(DBusMessageIter *iter)
+{
+ int err = -ECANCELED;
+ char *key;
+
+ /* If the given passphrase is malformed wpa_s returns
+ * "invalid message format" but this error should be interpreted as
+ * invalid-key.
+ */
+ while (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_STRING) {
+ dbus_message_iter_get_basic(iter, &key);
+ if (strncmp(key, "psk", 3) == 0 ||
+ strncmp(key, "wep_key", 7) == 0 ||
+ strcmp(key, "invalid message format") == 0) {
+ err = -ENOKEY;
+ break;
+ }
+ dbus_message_iter_next(iter);
+ }
+
+ return err;
}
static void interface_select_network_result(const char *error,
DBusMessageIter *iter, void *user_data)
{
struct interface_connect_data *data = user_data;
+ int err;
SUPPLICANT_DBG("");
+ err = 0;
+ if (error != NULL) {
+ SUPPLICANT_DBG("SelectNetwork error %s", error);
+ err = parse_supplicant_error(iter);
+ }
+
+ if (data->callback != NULL)
+ data->callback(err, data->interface, data->user_data);
+
+ g_free(data->ssid);
dbus_free(data);
}
struct interface_connect_data *data = user_data;
GSupplicantInterface *interface = data->interface;
const char *path;
+ int err;
if (error != NULL)
goto error;
return;
error:
+ SUPPLICANT_DBG("AddNetwork error %s", error);
+ err = parse_supplicant_error(iter);
+ if (data->callback != NULL)
+ data->callback(err, data->interface, data->user_data);
+
g_free(interface->network_path);
interface->network_path = NULL;
+ g_free(data->ssid);
g_free(data);
}
}
}
+static dbus_bool_t is_psk_raw_key(const char *psk)
+{
+ int i;
+
+ /* A raw key is always 64 bytes length... */
+ if (strlen(psk) != 64)
+ return FALSE;
+
+ /* ... and its content is in hex representation */
+ for (i = 0; i < 64; i++)
+ if (!isxdigit((unsigned char) psk[i]))
+ return FALSE;
+
+ return TRUE;
+}
+
static void add_network_security_psk(DBusMessageIter *dict,
GSupplicantSSID *ssid)
{
- if (ssid->passphrase && strlen(ssid->passphrase) > 0)
+ if (ssid->passphrase && strlen(ssid->passphrase) > 0) {
+
+ if (is_psk_raw_key(ssid->passphrase) == TRUE)
+ supplicant_dbus_property_append_fixed_array(dict,
+ "psk", DBUS_TYPE_BYTE,
+ &ssid->passphrase, 64);
+ else
supplicant_dbus_dict_append_basic(dict, "psk",
- DBUS_TYPE_STRING,
+ DBUS_TYPE_STRING,
&ssid->passphrase);
+ }
}
static void add_network_security_tls(DBusMessageIter *dict,
* The 2nd phase authentication method
* The 2nd phase passphrase
*
- * The Client certificate is optional although strongly required
+ * The Client certificate is optional although strongly recommended
* When setting it, we need in addition
* The Client private key file
* The Client private key file password
if (ssid->passphrase == NULL)
return;
- if (ssid->ca_cert_path == NULL)
- return;
-
if (ssid->phase2_auth == NULL)
return;
}
- phase2_auth = g_strdup_printf("auth=%s", ssid->phase2_auth);
+ if (g_str_has_prefix(ssid->phase2_auth, "EAP-") == TRUE) {
+ phase2_auth = g_strdup_printf("autheap=%s",
+ ssid->phase2_auth + strlen("EAP-"));
+ } else
+ phase2_auth = g_strdup_printf("auth=%s", ssid->phase2_auth);
supplicant_dbus_dict_append_basic(dict, "password",
DBUS_TYPE_STRING,
&ssid->passphrase);
- supplicant_dbus_dict_append_basic(dict, "ca_cert",
+ if (ssid->ca_cert_path)
+ supplicant_dbus_dict_append_basic(dict, "ca_cert",
DBUS_TYPE_STRING,
&ssid->ca_cert_path);
g_free(eap_value);
}
+static void add_network_security_ciphers(DBusMessageIter *dict,
+ GSupplicantSSID *ssid)
+{
+ unsigned int p_cipher, g_cipher, i;
+ char *pairwise, *group;
+ char *pair_ciphers[4];
+ char *group_ciphers[5];
+
+ p_cipher = ssid->pairwise_cipher;
+ g_cipher = ssid->group_cipher;
+
+ if (p_cipher == 0 && g_cipher == 0)
+ return;
+
+ i = 0;
+
+ if (p_cipher & G_SUPPLICANT_PAIRWISE_CCMP)
+ pair_ciphers[i++] = "CCMP";
+
+ if (p_cipher & G_SUPPLICANT_PAIRWISE_TKIP)
+ pair_ciphers[i++] = "TKIP";
+
+ if (p_cipher & G_SUPPLICANT_PAIRWISE_NONE)
+ pair_ciphers[i++] = "NONE";
+
+ pair_ciphers[i] = NULL;
+
+ i = 0;
+
+ if (g_cipher & G_SUPPLICANT_GROUP_CCMP)
+ group_ciphers[i++] = "CCMP";
+
+ if (g_cipher & G_SUPPLICANT_GROUP_TKIP)
+ group_ciphers[i++] = "TKIP";
+
+ if (g_cipher & G_SUPPLICANT_GROUP_WEP104)
+ group_ciphers[i++] = "WEP104";
+
+ if (g_cipher & G_SUPPLICANT_GROUP_WEP40)
+ group_ciphers[i++] = "WEP40";
+
+ group_ciphers[i] = NULL;
+
+ pairwise = g_strjoinv(" ", pair_ciphers);
+ group = g_strjoinv(" ", group_ciphers);
+
+ SUPPLICANT_DBG("cipher %s %s", pairwise, group);
+
+ supplicant_dbus_dict_append_basic(dict, "pairwise",
+ DBUS_TYPE_STRING,
+ &pairwise);
+ supplicant_dbus_dict_append_basic(dict, "group",
+ DBUS_TYPE_STRING,
+ &group);
+
+ g_free(pairwise);
+ g_free(group);
+}
+
+static void add_network_security_proto(DBusMessageIter *dict,
+ GSupplicantSSID *ssid)
+{
+ unsigned int protocol, i;
+ char *proto;
+ char *protos[3];
+
+ protocol = ssid->protocol;
+
+ if (protocol == 0)
+ return;
+
+ i = 0;
+
+ if (protocol & G_SUPPLICANT_PROTO_RSN)
+ protos[i++] = "RSN";
+
+ if (protocol & G_SUPPLICANT_PROTO_WPA)
+ protos[i++] = "WPA";
+
+ protos[i] = NULL;
+
+ proto = g_strjoinv(" ", protos);
+
+ SUPPLICANT_DBG("proto %s", proto);
+
+ supplicant_dbus_dict_append_basic(dict, "proto",
+ DBUS_TYPE_STRING,
+ &proto);
+
+ g_free(proto);
+}
+
static void add_network_security(DBusMessageIter *dict, GSupplicantSSID *ssid)
{
char *key_mgmt;
case G_SUPPLICANT_SECURITY_WEP:
key_mgmt = "NONE";
add_network_security_wep(dict, ssid);
+ add_network_security_ciphers(dict, ssid);
break;
case G_SUPPLICANT_SECURITY_PSK:
key_mgmt = "WPA-PSK";
add_network_security_psk(dict, ssid);
+ add_network_security_ciphers(dict, ssid);
+ add_network_security_proto(dict, ssid);
break;
case G_SUPPLICANT_SECURITY_IEEE8021X:
key_mgmt = "WPA-EAP";
add_network_security_eap(dict, ssid);
+ add_network_security_ciphers(dict, ssid);
+ add_network_security_proto(dict, ssid);
break;
}
DBUS_TYPE_STRING, &key_mgmt);
}
+static void add_network_mode(DBusMessageIter *dict, GSupplicantSSID *ssid)
+{
+ dbus_uint32_t mode;
+
+ switch (ssid->mode) {
+ case G_SUPPLICANT_MODE_UNKNOWN:
+ case G_SUPPLICANT_MODE_INFRA:
+ mode = 0;
+ break;
+ case G_SUPPLICANT_MODE_IBSS:
+ mode = 1;
+ break;
+ case G_SUPPLICANT_MODE_MASTER:
+ mode = 2;
+ break;
+ }
+
+ supplicant_dbus_dict_append_basic(dict, "mode",
+ DBUS_TYPE_UINT32, &mode);
+}
+
static void interface_add_network_params(DBusMessageIter *iter, void *user_data)
{
DBusMessageIter dict;
- dbus_uint32_t scan_ssid = 1;
struct interface_connect_data *data = user_data;
GSupplicantSSID *ssid = data->ssid;
supplicant_dbus_dict_open(iter, &dict);
- supplicant_dbus_dict_append_basic(&dict, "scan_ssid",
- DBUS_TYPE_UINT32, &scan_ssid);
+ if (ssid->scan_ssid)
+ supplicant_dbus_dict_append_basic(&dict, "scan_ssid",
+ DBUS_TYPE_UINT32, &ssid->scan_ssid);
+
+ if (ssid->freq)
+ supplicant_dbus_dict_append_basic(&dict, "frequency",
+ DBUS_TYPE_UINT32, &ssid->freq);
+
+ if (ssid->bgscan != NULL)
+ supplicant_dbus_dict_append_basic(&dict, "bgscan",
+ DBUS_TYPE_STRING, &ssid->bgscan);
+
+ add_network_mode(&dict, ssid);
add_network_security(&dict, ssid);
supplicant_dbus_dict_close(iter, &dict);
}
+static void interface_wps_start_result(const char *error,
+ DBusMessageIter *iter, void *user_data)
+{
+ struct interface_connect_data *data = user_data;
+
+ SUPPLICANT_DBG("");
+ if (error != NULL)
+ SUPPLICANT_DBG("error: %s", error);
+
+ g_free(data->ssid);
+ dbus_free(data);
+}
+
+static void interface_add_wps_params(DBusMessageIter *iter, void *user_data)
+{
+ struct interface_connect_data *data = user_data;
+ GSupplicantSSID *ssid = data->ssid;
+ const char *role = "enrollee", *type;
+ DBusMessageIter dict;
+
+ SUPPLICANT_DBG("");
+
+ supplicant_dbus_dict_open(iter, &dict);
+
+ supplicant_dbus_dict_append_basic(&dict, "Role",
+ DBUS_TYPE_STRING, &role);
+
+ type = "pbc";
+ if (ssid->pin_wps != NULL) {
+ type = "pin";
+ supplicant_dbus_dict_append_basic(&dict, "Pin",
+ DBUS_TYPE_STRING, &ssid->pin_wps);
+ }
+
+ supplicant_dbus_dict_append_basic(&dict, "Type",
+ DBUS_TYPE_STRING, &type);
+
+ supplicant_dbus_dict_close(iter, &dict);
+}
+
+static void wps_start(const char *error, DBusMessageIter *iter, void *user_data)
+{
+ struct interface_connect_data *data = user_data;
+
+ SUPPLICANT_DBG("");
+
+ if (error != NULL) {
+ SUPPLICANT_DBG("error: %s", error);
+ g_free(data->ssid);
+ dbus_free(data);
+ return;
+ }
+
+ supplicant_dbus_method_call(data->interface->path,
+ SUPPLICANT_INTERFACE ".Interface.WPS", "Start",
+ interface_add_wps_params,
+ interface_wps_start_result, data);
+}
+
+static void wps_process_credentials(DBusMessageIter *iter, void *user_data)
+{
+ dbus_bool_t credentials = TRUE;
+
+ SUPPLICANT_DBG("");
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &credentials);
+}
+
+
int g_supplicant_interface_connect(GSupplicantInterface *interface,
GSupplicantSSID *ssid,
GSupplicantInterfaceCallback callback,
data->ssid = ssid;
data->user_data = user_data;
- ret = supplicant_dbus_method_call(interface->path,
+ if (ssid->use_wps == TRUE) {
+ g_free(interface->wps_cred.key);
+ memset(&interface->wps_cred, 0,
+ sizeof(struct _GSupplicantWpsCredentials));
+
+ ret = supplicant_dbus_property_set(interface->path,
+ SUPPLICANT_INTERFACE ".Interface.WPS",
+ "ProcessCredentials", DBUS_TYPE_BOOLEAN_AS_STRING,
+ wps_process_credentials, wps_start, data);
+ } else
+ ret = supplicant_dbus_method_call(interface->path,
SUPPLICANT_INTERFACE ".Interface", "AddNetwork",
interface_add_network_params,
interface_add_network_result, data);
+
if (ret < 0)
return ret;
if (error != NULL && data->callback != NULL)
data->callback(-EIO, data->interface, data->user_data);
+ /* If we are disconnecting from previous WPS successful
+ * association. i.e.: it did not went through AddNetwork,
+ * and interface->network_path was never set. */
+ if (data->interface->network_path == NULL)
+ return;
+
network_remove(data);
}
callback_system_killed();
if (interface_table != NULL) {
- g_hash_table_foreach(interface_table,
+ g_hash_table_foreach(interface_table,
unregister_remove_interface, NULL);
g_hash_table_destroy(interface_table);
interface_table = NULL;