*
* WPA supplicant library with GLib integration
*
- * Copyright (C) 2010 Intel Corporation. All rights reserved.
+ * Copyright (C) 2012 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
dbus_int16_t signal;
GSupplicantMode mode;
GSupplicantSecurity security;
+ dbus_bool_t rsn_selected;
+ unsigned int wpa_keymgmt;
+ unsigned int wpa_pairwise;
+ unsigned int wpa_group;
+ unsigned int rsn_keymgmt;
+ unsigned int rsn_pairwise;
+ unsigned int rsn_group;
unsigned int keymgmt;
- unsigned int pairwise;
- unsigned int group;
dbus_bool_t privacy;
dbus_bool_t psk;
dbus_bool_t ieee8021x;
GSupplicantMode mode;
GSupplicantSecurity security;
dbus_bool_t wps;
+ unsigned int wps_capabilities;
GHashTable *bss_table;
GHashTable *config_table;
};
g_hash_table_destroy(interface->net_mapping);
g_hash_table_destroy(interface->network_table);
+ if (interface->scan_callback != NULL) {
+ SUPPLICANT_DBG("call interface %p callback %p scanning %d",
+ interface, interface->scan_callback,
+ interface->scanning);
+
+ interface->scan_callback(-EIO, interface, interface->scan_data);
+ interface->scan_callback = NULL;
+ interface->scan_data = NULL;
+
+ if (interface->scanning == TRUE) {
+ interface->scanning = FALSE;
+ callback_scan_finished(interface);
+ }
+ }
+
callback_interface_removed(interface);
g_free(interface->wps_cred.key);
if (interface == NULL)
return 0;
+ if (interface->max_scan_ssids == 0)
+ return WPAS_MAX_SCAN_SSIDS;
+
return interface->max_scan_ssids;
}
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;
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;
char *group;
group = create_group(bss);
+ SUPPLICANT_DBG("New group created: %s", group);
+
if (group == NULL)
return;
network = g_hash_table_lookup(interface->network_table, group);
if (network != NULL) {
g_free(group);
+ SUPPLICANT_DBG("Network %s already exist", network->name);
+
goto done;
}
network->frequency = bss->frequency;
network->best_bss = bss;
- network->wps = FALSE;
- if ((bss->keymgmt & G_SUPPLICANT_KEYMGMT_WPS) != 0)
- network->wps = TRUE;
+ SUPPLICANT_DBG("New network %s created", network->name);
network->bss_table = g_hash_table_new_full(g_str_hash, g_str_equal,
NULL, remove_bss);
callback_network_added(network);
done:
+ /* We update network's WPS properties if only bss provides WPS. */
+ if ((bss->keymgmt & G_SUPPLICANT_KEYMGMT_WPS) != 0) {
+ network->wps = TRUE;
+ network->wps_capabilities |= bss->wps_capabilities;
+ }
+
if (bss->signal > network->signal) {
network->signal = bss->signal;
network->best_bss = bss;
static void bss_keymgmt(DBusMessageIter *iter, void *user_data)
{
- struct g_supplicant_bss *bss = user_data;
+ unsigned int *keymgmt = user_data;
const char *str = NULL;
int i;
for (i = 0; keymgmt_map[i].str != NULL; i++)
if (strcmp(str, keymgmt_map[i].str) == 0) {
- bss->keymgmt |= keymgmt_map[i].val;
+ SUPPLICANT_DBG("Keymgmt: %s", str);
+ *keymgmt |= keymgmt_map[i].val;
break;
}
}
static void bss_group(DBusMessageIter *iter, void *user_data)
{
- struct g_supplicant_bss *bss = user_data;
+ unsigned int *group = user_data;
const char *str = NULL;
int i;
for (i = 0; group_map[i].str != NULL; i++)
if (strcmp(str, group_map[i].str) == 0) {
- bss->group |= group_map[i].val;
+ SUPPLICANT_DBG("Group: %s", str);
+ *group |= group_map[i].val;
break;
}
}
static void bss_pairwise(DBusMessageIter *iter, void *user_data)
{
- struct g_supplicant_bss *bss = user_data;
+ unsigned int *pairwise = user_data;
const char *str = NULL;
int i;
for (i = 0; pairwise_map[i].str != NULL; i++)
if (strcmp(str, pairwise_map[i].str) == 0) {
- bss->pairwise |= pairwise_map[i].val;
+ SUPPLICANT_DBG("Pairwise: %s", str);
+ *pairwise |= pairwise_map[i].val;
break;
}
}
static void bss_wpa(const char *key, DBusMessageIter *iter,
void *user_data)
{
- if (g_strcmp0(key, "KeyMgmt") == 0)
- supplicant_dbus_array_foreach(iter, bss_keymgmt, user_data);
- else if (g_strcmp0(key, "Group") == 0)
- supplicant_dbus_array_foreach(iter, bss_group, user_data);
- else if (g_strcmp0(key, "Pairwise") == 0)
- supplicant_dbus_array_foreach(iter, bss_pairwise, user_data);
+ struct g_supplicant_bss *bss = user_data;
+ unsigned int value = 0;
+ SUPPLICANT_DBG("Key: %s", key);
+
+ if (g_strcmp0(key, "KeyMgmt") == 0) {
+ supplicant_dbus_array_foreach(iter, bss_keymgmt, &value);
+
+ if (bss->rsn_selected == TRUE)
+ bss->rsn_keymgmt = value;
+ else
+ bss->wpa_keymgmt = value;
+ } else if (g_strcmp0(key, "Group") == 0) {
+ supplicant_dbus_array_foreach(iter, bss_group, &value);
+
+ if (bss->rsn_selected == TRUE)
+ bss->rsn_group = value;
+ else
+ bss->wpa_group = value;
+ } else if (g_strcmp0(key, "Pairwise") == 0) {
+ supplicant_dbus_array_foreach(iter, bss_pairwise, &value);
+
+ if (bss->rsn_selected == TRUE)
+ bss->rsn_pairwise = value;
+ else
+ bss->wpa_pairwise = value;
+ }
}
static unsigned int get_tlv(unsigned char *ie, unsigned int ie_size,
if (ie == NULL || ie_len < 2)
return;
+ bss->wps_capabilities = 0;
+ bss->keymgmt = 0;
+
for (ie_end = ie + ie_len; ie < ie_end && ie + ie[1] + 1 <= ie_end;
ie += ie[1] + 2) {
}
}
+static void bss_compute_security(struct g_supplicant_bss *bss)
+{
+ /*
+ * Combining RSN and WPA keymgmt
+ * We combine it since parsing IEs might have set something for WPS. */
+ bss->keymgmt |= bss->rsn_keymgmt | bss->wpa_keymgmt;
+
+ bss->ieee8021x = FALSE;
+ bss->psk = FALSE;
+
+ if (bss->keymgmt &
+ (G_SUPPLICANT_KEYMGMT_WPA_EAP |
+ G_SUPPLICANT_KEYMGMT_WPA_FT_EAP |
+ G_SUPPLICANT_KEYMGMT_WPA_EAP_256))
+ bss->ieee8021x = TRUE;
+
+ if (bss->keymgmt &
+ (G_SUPPLICANT_KEYMGMT_WPA_PSK |
+ G_SUPPLICANT_KEYMGMT_WPA_FT_PSK |
+ G_SUPPLICANT_KEYMGMT_WPA_PSK_256))
+ bss->psk = TRUE;
+
+ 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;
-
- add_bss_to_network(bss);
+ if (key == NULL)
return;
- }
if (g_strcmp0(key, "BSSID") == 0) {
DBusMessageIter array;
dbus_message_iter_get_basic(iter, &privacy);
bss->privacy = privacy;
- } else if ((g_strcmp0(key, "RSN") == 0) ||
- (g_strcmp0(key, "WPA") == 0)) {
- supplicant_dbus_property_foreach(iter, bss_wpa, bss);
+ } else if (g_strcmp0(key, "RSN") == 0) {
+ bss->rsn_selected = TRUE;
- if (bss->keymgmt &
- (G_SUPPLICANT_KEYMGMT_WPA_EAP |
- G_SUPPLICANT_KEYMGMT_WPA_FT_EAP |
- G_SUPPLICANT_KEYMGMT_WPA_EAP_256))
- bss->ieee8021x = TRUE;
+ supplicant_dbus_property_foreach(iter, bss_wpa, bss);
+ } else if (g_strcmp0(key, "WPA") == 0) {
+ bss->rsn_selected = FALSE;
- if (bss->keymgmt &
- (G_SUPPLICANT_KEYMGMT_WPA_PSK |
- G_SUPPLICANT_KEYMGMT_WPA_FT_PSK |
- G_SUPPLICANT_KEYMGMT_WPA_PSK_256))
- bss->psk = TRUE;
+ supplicant_dbus_property_foreach(iter, bss_wpa, bss);
} else if (g_strcmp0(key, "IEs") == 0)
bss_process_ies(iter, bss);
else
return;
supplicant_dbus_property_foreach(iter, bss_property, bss);
- bss_property(NULL, NULL, bss);
+
+ bss_compute_security(bss);
+ add_or_replace_bss_to_network(bss);
}
static void interface_bss_added_without_keys(DBusMessageIter *iter,
supplicant_dbus_property_get_all(bss->path,
SUPPLICANT_INTERFACE ".BSS",
bss_property, bss);
+
+ bss_compute_security(bss);
+ add_or_replace_bss_to_network(bss);
}
static void update_signal(gpointer key, gpointer value,
{
GSupplicantInterface *interface;
GSupplicantNetwork *network;
+ GSupplicantSecurity old_security;
struct g_supplicant_bss *bss;
SUPPLICANT_DBG("");
supplicant_dbus_property_foreach(iter, bss_property, bss);
+ old_security = network->security;
+ bss_compute_security(bss);
+
+ if (old_security != bss->security) {
+ struct g_supplicant_bss *new_bss;
+
+ SUPPLICANT_DBG("New network security for %s", bss->ssid);
+
+ /* Security change policy:
+ * - we first copy the current bss into a new one with
+ * its own pointer (path)
+ * - we remove the current bss related network which will
+ * tell the plugin about such removal. This is done due
+ * to the fact that a security change means a group change
+ * so a complete network change.
+ * (current bss becomes invalid as well)
+ * - we add the new bss: it adds new network and tell the
+ * plugin about it. */
+
+ new_bss = g_try_new0(struct g_supplicant_bss, 1);
+ if (new_bss == NULL)
+ return;
+
+ memcpy(new_bss, bss, sizeof(struct g_supplicant_bss));
+ new_bss->path = g_strdup(bss->path);
+
+ g_hash_table_remove(interface->network_table, network->group);
+
+ add_or_replace_bss_to_network(new_bss);
+
+ return;
+ }
+
if (bss->signal == network->signal)
return;
SUPPLICANT_DBG("");
if (error != NULL) {
- g_critical("error %s", error);
+ g_warning("error %s", error);
err = -EIO;
goto done;
}
if (error != NULL) {
SUPPLICANT_DBG("Interface not created yet");
- err = -EIO;
goto create;
}
}
if (data != NULL && data->scan_params != NULL)
- g_free(data->scan_params);
+ g_supplicant_free_scan_params(data->scan_params);
dbus_free(data);
}
unsigned int freq;
int i;
- for (i = 0; i < G_SUPPLICANT_MAX_FAST_SCAN; i++) {
+ for (i = 0; i < scan_data->num_ssids; i++) {
freq = scan_data->freqs[i];
if (!freq)
break;
static void append_ssids(DBusMessageIter *iter, void *user_data)
{
GSupplicantScanParams *scan_data = user_data;
- int i;
+ GSList *list;
+
+ for (list = scan_data->ssids; list; list = list->next) {
+ struct scan_ssid *scan_ssid = list->data;
- for (i = 0; i < scan_data->num_ssids; i++)
- append_ssid(iter, scan_data->ssids[i].ssid,
- scan_data->ssids[i].ssid_len);
+ append_ssid(iter, scan_ssid->ssid, scan_ssid->ssid_len);
+ }
}
static void supplicant_add_scan_frequency(DBusMessageIter *dict,
DBusMessageIter entry, value, array;
const char *key = "Channels";
- if (scan_params->freqs[0] != 0) {
+ if (scan_params->freqs && scan_params->freqs[0] != 0) {
dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
NULL, &entry);
return TRUE;
}
+static unsigned char hexchar2bin(char c)
+{
+ if ((c >= '0') && (c <= '9'))
+ return c - '0';
+ else if ((c >= 'A') && (c <= 'F'))
+ return c - 'A' + 10;
+ else if ((c >= 'a') && (c <= 'f'))
+ return c - 'a' + 10;
+ else
+ return c;
+}
+
+static void hexstring2bin(const char *string, unsigned char *data, size_t data_len)
+{
+ size_t i;
+
+ for (i = 0; i < data_len; i++)
+ data[i] = (hexchar2bin(string[i * 2 + 0]) << 4 |
+ hexchar2bin(string[i * 2 + 1]) << 0);
+}
+
static void add_network_security_psk(DBusMessageIter *dict,
GSupplicantSSID *ssid)
{
if (ssid->passphrase && strlen(ssid->passphrase) > 0) {
+ const char *key = "psk";
- 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,
+ if (is_psk_raw_key(ssid->passphrase) == TRUE) {
+ unsigned char data[32];
+ unsigned char *datap = data;
+
+ /* The above pointer alias is required by D-Bus because
+ * with D-Bus and GCC, non-heap-allocated arrays cannot
+ * be passed directly by their base pointer. */
+
+ hexstring2bin(ssid->passphrase, datap, sizeof(data));
+
+ supplicant_dbus_dict_append_fixed_array(dict,
+ key, DBUS_TYPE_BYTE,
+ &datap, sizeof(data));
+ } else
+ supplicant_dbus_dict_append_basic(dict,
+ key, DBUS_TYPE_STRING,
&ssid->passphrase);
}
}
/* 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)
+ if (data->interface->network_path == NULL) {
+ dbus_free(data);
return;
+ }
network_remove(data);
}