service: Simplify nameserver route adding and removing
[framework/connectivity/connman.git] / gsupplicant / supplicant.c
index 5db1fcd..decf5fa 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  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
@@ -182,12 +182,18 @@ struct g_supplicant_bss {
        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;
+       unsigned int wps_capabilities;
 };
 
 struct _GSupplicantNetwork {
@@ -203,6 +209,7 @@ struct _GSupplicantNetwork {
        GSupplicantMode mode;
        GSupplicantSecurity security;
        dbus_bool_t wps;
+       unsigned int wps_capabilities;
        GHashTable *bss_table;
        GHashTable *config_table;
 };
@@ -740,6 +747,9 @@ unsigned int g_supplicant_interface_get_max_scan_ssids(
        if (interface == NULL)
                return 0;
 
+       if (interface->max_scan_ssids == 0)
+               return WPAS_MAX_SCAN_SSIDS;
+
        return interface->max_scan_ssids;
 }
 
@@ -859,6 +869,39 @@ dbus_bool_t g_supplicant_network_get_wps(GSupplicantNetwork *network)
        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;
@@ -1061,19 +1104,23 @@ 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;
        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;
        }
 
@@ -1096,9 +1143,7 @@ static void add_bss_to_network(struct g_supplicant_bss *bss)
        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);
@@ -1112,6 +1157,12 @@ static void add_bss_to_network(struct g_supplicant_bss *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;
@@ -1139,7 +1190,7 @@ static void bss_rates(DBusMessageIter *iter, void *user_data)
 
 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;
 
@@ -1149,14 +1200,15 @@ static void bss_keymgmt(DBusMessageIter *iter, void *user_data)
 
        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;
 
@@ -1166,14 +1218,15 @@ static void bss_group(DBusMessageIter *iter, void *user_data)
 
        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;
 
@@ -1183,7 +1236,8 @@ static void bss_pairwise(DBusMessageIter *iter, void *user_data)
 
        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;
                }
 }
@@ -1191,13 +1245,33 @@ static void bss_pairwise(DBusMessageIter *iter, void *user_data)
 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,
@@ -1248,13 +1322,19 @@ static void bss_process_ies(DBusMessageIter *iter, void *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);
@@ -1262,6 +1342,9 @@ static void bss_process_ies(DBusMessageIter *iter, void *user_data)
        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) {
 
@@ -1271,14 +1354,69 @@ static void bss_process_ies(DBusMessageIter *iter, void *user_data)
 
                SUPPLICANT_DBG("IE: match WPS_OUI");
 
-               if (get_tlv(&ie[6], ie[1],
-                               WPS_VERSION_TLV) == WPS_VERSION &&
-                       get_tlv(&ie[6], ie[1],
-                               WPS_STATE_TLV) != 0)
+               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)
+{
+       /*
+        * 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)
 {
@@ -1289,19 +1427,8 @@ static void bss_property(const char *key, DBusMessageIter *iter,
 
        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;
@@ -1373,21 +1500,14 @@ static void bss_property(const char *key, DBusMessageIter *iter,
 
                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
@@ -1448,7 +1568,9 @@ static void interface_bss_added_with_keys(DBusMessageIter *iter,
                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,
@@ -1465,6 +1587,9 @@ 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,
@@ -1948,6 +2073,7 @@ static void signal_bss_changed(const char *path, DBusMessageIter *iter)
 {
        GSupplicantInterface *interface;
        GSupplicantNetwork *network;
+       GSupplicantSecurity old_security;
        struct g_supplicant_bss *bss;
 
        SUPPLICANT_DBG("");
@@ -1966,6 +2092,39 @@ static void signal_bss_changed(const char *path, DBusMessageIter *iter)
 
        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;
 
@@ -2263,7 +2422,7 @@ static void interface_create_result(const char *error,
        SUPPLICANT_DBG("");
 
        if (error != NULL) {
-               g_critical("error %s", error);
+               g_warning("error %s", error);
                err = -EIO;
                goto done;
        }
@@ -2336,7 +2495,6 @@ static void interface_get_result(const char *error,
 
        if (error != NULL) {
                SUPPLICANT_DBG("Interface not created yet");
-               err = -EIO;
                goto create;
        }
 
@@ -2505,7 +2663,7 @@ static void interface_scan_result(const char *error,
        }
 
        if (data != NULL && data->scan_params != NULL)
-               g_free(data->scan_params);
+               g_supplicant_free_scan_params(data->scan_params);
 
        dbus_free(data);
 }
@@ -2530,7 +2688,7 @@ static void add_scan_frequencies(DBusMessageIter *iter,
        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;
@@ -2555,11 +2713,13 @@ static void append_ssid(DBusMessageIter *iter,
 static void append_ssids(DBusMessageIter *iter, void *user_data)
 {
        GSupplicantScanParams *scan_data = user_data;
-       int i;
+       GSList *list;
 
-       for (i = 0; i < scan_data->num_ssids; i++)
-               append_ssid(iter, scan_data->ssids[i].ssid,
-                                       scan_data->ssids[i].ssid_len);
+       for (list = scan_data->ssids; list; list = list->next) {
+               struct scan_ssid *scan_ssid = list->data;
+
+               append_ssid(iter, scan_ssid->ssid, scan_ssid->ssid_len);
+       }
 }
 
 static void supplicant_add_scan_frequency(DBusMessageIter *dict,
@@ -2570,7 +2730,7 @@ 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);
 
@@ -2684,10 +2844,15 @@ 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", 4) == 0 ||
-                       strncmp(key, "wep_key", 7) == 0) {
+               if (strncmp(key, "psk", 3) == 0 ||
+                               strncmp(key, "wep_key", 7) == 0 ||
+                               strcmp(key, "invalid message format") == 0) {
                        err = -ENOKEY;
                        break;
                }
@@ -2839,18 +3004,49 @@ static dbus_bool_t is_psk_raw_key(const char *psk)
        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);
        }
 }
@@ -3323,8 +3519,10 @@ static void interface_disconnect_result(const char *error,
        /* 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);
 }
@@ -3483,7 +3681,7 @@ void g_supplicant_unregister(const GSupplicantCallbacks *callbacks)
                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;