#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;
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;
- dbus_bool_t wps;
- GHashTable *bss_table;
- GHashTable *config_table;
-};
-
struct g_supplicant_bss {
GSupplicantInterface *interface;
char *path;
dbus_bool_t ieee8021x;
};
+struct _GSupplicantNetwork {
+ GSupplicantInterface *interface;
+ char *path;
+ char *group;
+ char *name;
+ unsigned char ssid[32];
+ unsigned int ssid_len;
+ dbus_int16_t signal;
+ struct g_supplicant_bss *best_bss;
+ GSupplicantMode mode;
+ GSupplicantSecurity security;
+ dbus_bool_t wps;
+ GHashTable *bss_table;
+ GHashTable *config_table;
+};
+
static inline void debug(const char *format, ...)
{
char str[256];
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);
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;
+}
+
GSupplicantInterface *g_supplicant_network_get_interface(
GSupplicantNetwork *network)
{
network->ssid_len = bss->ssid_len;
memcpy(network->ssid, bss->ssid, bss->ssid_len);
network->signal = bss->signal;
+ network->best_bss = bss;
network->wps = FALSE;
if ((bss->keymgmt & G_SUPPLICANT_KEYMGMT_WPS) != 0)
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);
if (ie == NULL || ie_len < 2)
return;
- for (ie_end = ie+ie_len; ie+ie[1]+1 <= ie_end; ie += ie[1]+2) {
+ 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;
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;
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;
interface_scan_params, interface_scan_result, data);
}
+static int parse_supplicant_error(DBusMessageIter *iter)
+{
+ int err = -ECANCELED;
+ char *key;
+
+ while (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_STRING) {
+ dbus_message_iter_get_basic(iter, &key);
+ if (strncmp(key, "psk", 4) == 0 ||
+ strncmp(key, "wep_key", 7) == 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);
}
}
+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;
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);
+
+ 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);
}