gsupplicant: Return max SSIDs to scan given by wpa_supplicant
[platform/upstream/connman.git] / gsupplicant / supplicant.c
index 2c1a928..e298bb5 100644 (file)
@@ -182,9 +182,14 @@ 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;
@@ -282,6 +287,8 @@ static GSupplicantState string2state(const char *state)
 
        if (g_str_equal(state, "unknown") == TRUE)
                return G_SUPPLICANT_STATE_UNKNOWN;
+       else if (g_str_equal(state, "interface_disabled") == TRUE)
+               return G_SUPPLICANT_STATE_DISABLED;
        else if (g_str_equal(state, "disconnected") == TRUE)
                return G_SUPPLICANT_STATE_DISCONNECTED;
        else if (g_str_equal(state, "inactive") == TRUE)
@@ -432,6 +439,21 @@ static void remove_interface(gpointer data)
        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);
@@ -631,6 +653,8 @@ static void interface_capability(const char *key, DBusMessageIter *iter,
                dbus_int32_t max_scan_ssid;
 
                dbus_message_iter_get_basic(iter, &max_scan_ssid);
+               if (max_scan_ssid < 1)
+                       max_scan_ssid = 1;
                interface->max_scan_ssids = max_scan_ssid;
 
        } else
@@ -742,9 +766,6 @@ 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;
 }
 
@@ -1138,13 +1159,6 @@ static void add_or_replace_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;
-
-               network->wps_capabilities |= bss->wps_capabilities;
-       }
-
        SUPPLICANT_DBG("New network %s created", network->name);
 
        network->bss_table = g_hash_table_new_full(g_str_hash, g_str_equal,
@@ -1159,6 +1173,12 @@ static void add_or_replace_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;
@@ -1186,7 +1206,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;
 
@@ -1196,14 +1216,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;
 
@@ -1213,14 +1234,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;
 
@@ -1230,7 +1252,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;
                }
 }
@@ -1238,13 +1261,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,
@@ -1315,6 +1358,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) {
 
@@ -1356,6 +1402,26 @@ static void bss_process_ies(DBusMessageIter *iter, void *user_data)
 
 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)
@@ -1450,21 +1516,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
@@ -1623,7 +1682,6 @@ static void interface_property(const char *key, DBusMessageIter *iter,
                debug_strvalmap("Mode capability", mode_capa_map,
                                                interface->mode_capa);
 
-               interface->ready = TRUE;
                callback_interface_added(interface);
                return;
        }
@@ -1640,6 +1698,10 @@ static void interface_property(const char *key, DBusMessageIter *iter,
                                interface->state = string2state(str);
                                callback_interface_state(interface);
                        }
+               if (interface->state == G_SUPPLICANT_STATE_DISABLED)
+                       interface->ready = FALSE;
+               else
+                       interface->ready = TRUE;
 
                SUPPLICANT_DBG("state %s (%d)", str, interface->state);
        } else if (g_strcmp0(key, "Scanning") == 0) {
@@ -2060,7 +2122,7 @@ static void signal_bss_changed(const char *path, DBusMessageIter *iter)
                /* Security change policy:
                 * - we first copy the current bss into a new one with
                 * its own pointer (path)
-                * - we remove the current bss related nework which will
+                * - 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.
@@ -2116,20 +2178,21 @@ static void wps_credentials(const char *key, DBusMessageIter *iter,
 
        if (g_strcmp0(key, "Key") == 0) {
                DBusMessageIter array;
-               unsigned char *key;
+               unsigned char *key_val;
                int key_len;
 
                dbus_message_iter_recurse(iter, &array);
-               dbus_message_iter_get_fixed_array(&array, &key, &key_len);
+               dbus_message_iter_get_fixed_array(&array, &key_val, &key_len);
 
                g_free(interface->wps_cred.key);
                interface->wps_cred.key = g_try_malloc0(
-                                               sizeof(char) * key_len+1);
+                                               sizeof(char) * key_len + 1);
 
                if (interface->wps_cred.key == NULL)
                        return;
 
-               memcpy(interface->wps_cred.key, key, sizeof(char) * key_len);
+               memcpy(interface->wps_cred.key, key_val,
+                                               sizeof(char) * key_len);
 
                SUPPLICANT_DBG("WPS key present");
        } else if (g_strcmp0(key, "SSID") == 0) {
@@ -2263,6 +2326,7 @@ static DBusHandlerResult g_supplicant_filter(DBusConnection *conn,
 
 struct supplicant_regdom {
        GSupplicantCountryCallback callback;
+       const char *alpha2;
        const void *user_data;
 };
 
@@ -2270,22 +2334,21 @@ static void country_result(const char *error,
                                DBusMessageIter *iter, void *user_data)
 {
        struct supplicant_regdom *regdom = user_data;
-       char *alpha2;
+       int result = 0;
 
        SUPPLICANT_DBG("Country setting result");
 
        if (user_data == NULL)
                return;
 
-       if (error == NULL) {
-               alpha2 = (char *)regdom->user_data;
-       } else {
+       if (error != NULL) {
                SUPPLICANT_DBG("Country setting failure %s", error);
-               alpha2 = NULL;
+               result = -EINVAL;
        }
 
        if (regdom->callback)
-               regdom->callback(alpha2);
+               regdom->callback(result, regdom->alpha2,
+                                       (void *) regdom->user_data);
 
        g_free(regdom);
 }
@@ -2293,11 +2356,9 @@ static void country_result(const char *error,
 static void country_params(DBusMessageIter *iter, void *user_data)
 {
        struct supplicant_regdom *regdom = user_data;
-       const char *country;
-
-       country = regdom->user_data;
 
-       dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &country);
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
+                                                       &regdom->alpha2);
 }
 
 int g_supplicant_set_country(const char *alpha2,
@@ -2316,6 +2377,7 @@ int g_supplicant_set_country(const char *alpha2,
                return -ENOMEM;
 
        regdom->callback = callback;
+       regdom->alpha2 = alpha2;
        regdom->user_data = user_data;
 
        return supplicant_dbus_property_set(SUPPLICANT_PATH, SUPPLICANT_INTERFACE,
@@ -2324,6 +2386,28 @@ int g_supplicant_set_country(const char *alpha2,
                                                regdom);
 }
 
+int g_supplicant_interface_set_country(GSupplicantInterface *interface,
+                                       GSupplicantCountryCallback callback,
+                                                       const char *alpha2,
+                                                       void *user_data)
+{
+       struct supplicant_regdom *regdom;
+
+       regdom = dbus_malloc0(sizeof(*regdom));
+       if (regdom == NULL)
+               return -ENOMEM;
+
+       regdom->callback = callback;
+       regdom->alpha2 = alpha2;
+       regdom->user_data = user_data;
+
+       return supplicant_dbus_property_set(interface->path,
+                               SUPPLICANT_INTERFACE ".Interface",
+                               "Country", DBUS_TYPE_STRING_AS_STRING,
+                               country_params, country_result,
+                                       regdom);
+}
+
 struct interface_data {
        GSupplicantInterface *interface;
        GSupplicantInterfaceCallback callback;
@@ -2353,6 +2437,13 @@ struct interface_scan_data {
        void *user_data;
 };
 
+struct interface_autoscan_data {
+       GSupplicantInterface *interface;
+       GSupplicantInterfaceCallback callback;
+       const char *autoscan_params;
+       void *user_data;
+};
+
 static void interface_create_property(const char *key, DBusMessageIter *iter,
                                                        void *user_data)
 {
@@ -2379,7 +2470,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;
        }
@@ -2452,7 +2543,6 @@ static void interface_get_result(const char *error,
 
        if (error != NULL) {
                SUPPLICANT_DBG("Interface not created yet");
-               err = -EIO;
                goto create;
        }
 
@@ -2609,12 +2699,20 @@ static void interface_scan_result(const char *error,
                                DBusMessageIter *iter, void *user_data)
 {
        struct interface_scan_data *data = user_data;
+       int err = 0;
 
        if (error != NULL) {
                SUPPLICANT_DBG("error %s", error);
+               err = -EIO;
+       }
+
+       /* A non ready interface cannot send/receive anything */
+       if (data->interface->ready == FALSE)
+               err = -ENOLINK;
 
+       if (err != 0) {
                if (data->callback != NULL)
-                       data->callback(-EIO, data->interface, data->user_data);
+                       data->callback(err, data->interface, data->user_data);
        } else {
                data->interface->scan_callback = data->callback;
                data->interface->scan_data = data->user_data;
@@ -2771,6 +2869,7 @@ int g_supplicant_interface_scan(GSupplicantInterface *interface,
        case G_SUPPLICANT_STATE_GROUP_HANDSHAKE:
                return -EBUSY;
        case G_SUPPLICANT_STATE_UNKNOWN:
+       case G_SUPPLICANT_STATE_DISABLED:
        case G_SUPPLICANT_STATE_DISCONNECTED:
        case G_SUPPLICANT_STATE_INACTIVE:
        case G_SUPPLICANT_STATE_SCANNING:
@@ -2797,6 +2896,58 @@ int g_supplicant_interface_scan(GSupplicantInterface *interface,
        return ret;
 }
 
+static void interface_autoscan_result(const char *error,
+                               DBusMessageIter *iter, void *user_data)
+{
+       struct interface_autoscan_data *data = user_data;
+       int err = 0;
+
+       if (error != NULL) {
+               SUPPLICANT_DBG("error %s", error);
+               err = -EIO;
+       }
+
+       if (data != NULL && data->callback != NULL)
+               data->callback(err, data->interface, data->user_data);
+
+       dbus_free(data);
+}
+
+static void interface_autoscan_params(DBusMessageIter *iter, void *user_data)
+{
+       struct interface_autoscan_data *data = user_data;
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
+                                                &data->autoscan_params);
+}
+
+int g_supplicant_interface_autoscan(GSupplicantInterface *interface,
+                                       const char *autoscan_data,
+                                       GSupplicantInterfaceCallback callback,
+                                                       void *user_data)
+{
+       struct interface_autoscan_data *data;
+       int ret;
+
+       data = dbus_malloc0(sizeof(*data));
+       if (data == NULL)
+               return -ENOMEM;
+
+       data->interface = interface;
+       data->callback = callback;
+       data->autoscan_params = autoscan_data;
+       data->user_data = user_data;
+
+       ret = supplicant_dbus_method_call(interface->path,
+                       SUPPLICANT_INTERFACE ".Interface", "AutoScan",
+                       interface_autoscan_params,
+                       interface_autoscan_result, data);
+       if (ret < 0)
+               dbus_free(data);
+
+       return ret;
+}
+
 static int parse_supplicant_error(DBusMessageIter *iter)
 {
        int err = -ECANCELED;
@@ -2962,18 +3113,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);
        }
 }
@@ -3403,8 +3585,12 @@ static void network_remove_result(const char *error,
 
        SUPPLICANT_DBG("");
 
-       if (error != NULL)
+       if (error != NULL) {
                result = -EIO;
+               if (g_strcmp0("org.freedesktop.DBus.Error.UnknownMethod",
+                                               error) == 0)
+                       result = -ECONNABORTED;
+       }
 
        if (data->callback != NULL)
                data->callback(result, data->interface, data->user_data);
@@ -3437,11 +3623,21 @@ static void interface_disconnect_result(const char *error,
                                DBusMessageIter *iter, void *user_data)
 {
        struct interface_data *data = user_data;
+       int result = 0;
 
        SUPPLICANT_DBG("");
 
-       if (error != NULL && data->callback != NULL)
-               data->callback(-EIO, data->interface, data->user_data);
+       if (error != NULL) {
+               result = -EIO;
+               if (g_strcmp0("org.freedesktop.DBus.Error.UnknownMethod",
+                                               error) == 0)
+                       result = -ECONNABORTED;
+       }
+
+       if (result < 0 && data->callback != NULL) {
+               data->callback(result, data->interface, data->user_data);
+               data->callback = NULL;
+       }
 
        /* If we are disconnecting from previous WPS successful
         * association. i.e.: it did not went through AddNetwork,
@@ -3451,7 +3647,10 @@ static void interface_disconnect_result(const char *error,
                return;
        }
 
-       network_remove(data);
+       if (result != -ECONNABORTED)
+               network_remove(data);
+       else
+               dbus_free(data);
 }
 
 int g_supplicant_interface_disconnect(GSupplicantInterface *interface,