wpa_supplicant: Fix seg fault in wpas_ctrl_radio_work_flush() in error case
[mech_eap.git] / wpa_supplicant / ctrl_iface.c
index 8e3931f..ec79de3 100644 (file)
@@ -3652,6 +3652,7 @@ static int p2p_ctrl_find(struct wpa_supplicant *wpa_s, char *cmd)
        unsigned int timeout = atoi(cmd);
        enum p2p_discovery_type type = P2P_FIND_START_WITH_FULL;
        u8 dev_id[ETH_ALEN], *_dev_id = NULL;
+       u8 dev_type[WPS_DEV_TYPE_LEN], *_dev_type = NULL;
        char *pos;
        unsigned int search_delay;
 
@@ -3668,6 +3669,14 @@ static int p2p_ctrl_find(struct wpa_supplicant *wpa_s, char *cmd)
                _dev_id = dev_id;
        }
 
+       pos = os_strstr(cmd, "dev_type=");
+       if (pos) {
+               pos += 9;
+               if (wps_dev_type_str2bin(pos, dev_type) < 0)
+                       return -1;
+               _dev_type = dev_type;
+       }
+
        pos = os_strstr(cmd, "delay=");
        if (pos) {
                pos += 6;
@@ -3675,8 +3684,8 @@ static int p2p_ctrl_find(struct wpa_supplicant *wpa_s, char *cmd)
        } else
                search_delay = wpas_p2p_search_delay(wpa_s);
 
-       return wpas_p2p_find(wpa_s, timeout, type, 0, NULL, _dev_id,
-                            search_delay);
+       return wpas_p2p_find(wpa_s, timeout, type, _dev_type != NULL, _dev_type,
+                            _dev_id, search_delay);
 }
 
 
@@ -4664,7 +4673,68 @@ static int p2p_ctrl_remove_client(struct wpa_supplicant *wpa_s, const char *cmd)
 #endif /* CONFIG_P2P */
 
 
+static int * freq_range_to_channel_list(struct wpa_supplicant *wpa_s, char *val)
+{
+       struct wpa_freq_range_list ranges;
+       int *freqs = NULL;
+       struct hostapd_hw_modes *mode;
+       u16 i;
+
+       if (wpa_s->hw.modes == NULL)
+               return NULL;
+
+       os_memset(&ranges, 0, sizeof(ranges));
+       if (freq_range_list_parse(&ranges, val) < 0)
+               return NULL;
+
+       for (i = 0; i < wpa_s->hw.num_modes; i++) {
+               int j;
+
+               mode = &wpa_s->hw.modes[i];
+               for (j = 0; j < mode->num_channels; j++) {
+                       unsigned int freq;
+
+                       if (mode->channels[j].flag & HOSTAPD_CHAN_DISABLED)
+                               continue;
+
+                       freq = mode->channels[j].freq;
+                       if (!freq_range_list_includes(&ranges, freq))
+                               continue;
+
+                       int_array_add_unique(&freqs, freq);
+               }
+       }
+
+       os_free(ranges.range);
+       return freqs;
+}
+
+
 #ifdef CONFIG_INTERWORKING
+
+static int ctrl_interworking_select(struct wpa_supplicant *wpa_s, char *param)
+{
+       int auto_sel = 0;
+       int *freqs = NULL;
+
+       if (param) {
+               char *pos;
+
+               auto_sel = os_strstr(param, "auto") != NULL;
+
+               pos = os_strstr(param, "freq=");
+               if (pos) {
+                       freqs = freq_range_to_channel_list(wpa_s, pos + 5);
+                       if (freqs == NULL)
+                               return -1;
+               }
+
+       }
+
+       return interworking_select(wpa_s, auto_sel, freqs);
+}
+
+
 static int ctrl_interworking_connect(struct wpa_supplicant *wpa_s, char *dst)
 {
        u8 bssid[ETH_ALEN];
@@ -4788,9 +4858,8 @@ static int gas_response_get(struct wpa_supplicant *wpa_s, char *cmd, char *buf,
        int used;
        char *pos;
        size_t resp_len, start, requested_len;
-
-       if (!wpa_s->last_gas_resp)
-               return -1;
+       struct wpabuf *resp;
+       int ret;
 
        used = hwaddr_aton2(cmd, addr);
        if (used < 0)
@@ -4801,11 +4870,18 @@ static int gas_response_get(struct wpa_supplicant *wpa_s, char *cmd, char *buf,
                pos++;
        dialog_token = atoi(pos);
 
-       if (os_memcmp(addr, wpa_s->last_gas_addr, ETH_ALEN) != 0 ||
-           dialog_token != wpa_s->last_gas_dialog_token)
+       if (wpa_s->last_gas_resp &&
+           os_memcmp(addr, wpa_s->last_gas_addr, ETH_ALEN) == 0 &&
+           dialog_token == wpa_s->last_gas_dialog_token)
+               resp = wpa_s->last_gas_resp;
+       else if (wpa_s->prev_gas_resp &&
+                os_memcmp(addr, wpa_s->prev_gas_addr, ETH_ALEN) == 0 &&
+                dialog_token == wpa_s->prev_gas_dialog_token)
+               resp = wpa_s->prev_gas_resp;
+       else
                return -1;
 
-       resp_len = wpabuf_len(wpa_s->last_gas_resp);
+       resp_len = wpabuf_len(resp);
        start = 0;
        requested_len = resp_len;
 
@@ -4826,9 +4902,24 @@ static int gas_response_get(struct wpa_supplicant *wpa_s, char *cmd, char *buf,
        if (requested_len * 2 + 1 > buflen)
                return os_snprintf(buf, buflen, "FAIL-Too long response");
 
-       return wpa_snprintf_hex(buf, buflen,
-                               wpabuf_head_u8(wpa_s->last_gas_resp) + start,
-                               requested_len);
+       ret = wpa_snprintf_hex(buf, buflen, wpabuf_head_u8(resp) + start,
+                              requested_len);
+
+       if (start + requested_len == resp_len) {
+               /*
+                * Free memory by dropping the response after it has been
+                * fetched.
+                */
+               if (resp == wpa_s->prev_gas_resp) {
+                       wpabuf_free(wpa_s->prev_gas_resp);
+                       wpa_s->prev_gas_resp = NULL;
+               } else {
+                       wpabuf_free(wpa_s->last_gas_resp);
+                       wpa_s->last_gas_resp = NULL;
+               }
+       }
+
+       return ret;
 }
 #endif /* CONFIG_INTERWORKING */
 
@@ -5167,6 +5258,10 @@ static void wpa_supplicant_ctrl_iface_flush(struct wpa_supplicant *wpa_s)
        wpas_p2p_stop_find(wpa_s);
        p2p_ctrl_flush(wpa_s);
        wpas_p2p_group_remove(wpa_s, "*");
+       wpas_p2p_service_flush(wpa_s);
+       wpa_s->global->p2p_disabled = 0;
+       wpa_s->global->p2p_per_sta_psk = 0;
+       wpa_s->conf->num_sec_device_types = 0;
 #endif /* CONFIG_P2P */
 
 #ifdef CONFIG_WPS_TESTING
@@ -5211,6 +5306,7 @@ static void wpa_supplicant_ctrl_iface_flush(struct wpa_supplicant *wpa_s)
        wpa_s->extra_blacklist_count = 0;
        wpa_supplicant_ctrl_iface_remove_network(wpa_s, "all");
        wpa_supplicant_ctrl_iface_remove_cred(wpa_s, "all");
+       wpa_config_flush_blobs(wpa_s->conf);
 
        wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_LIFETIME, 43200);
        wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_REAUTH_THRESHOLD, 70);
@@ -5381,6 +5477,9 @@ void wpas_ctrl_radio_work_flush(struct wpa_supplicant *wpa_s)
 {
        struct wpa_radio_work *work, *tmp;
 
+       if (!wpa_s || !wpa_s->radio)
+               return;
+
        dl_list_for_each_safe(work, tmp, &wpa_s->radio->work,
                              struct wpa_radio_work, list) {
                struct wpa_external_work *ework;
@@ -5410,37 +5509,12 @@ static void wpas_ctrl_eapol_response(void *eloop_ctx, void *timeout_ctx)
 
 static int set_scan_freqs(struct wpa_supplicant *wpa_s, char *val)
 {
-       struct wpa_freq_range_list ranges;
        int *freqs = NULL;
-       struct hostapd_hw_modes *mode;
-       u16 i;
-
-       if (wpa_s->hw.modes == NULL)
-               return -1;
 
-       os_memset(&ranges, 0, sizeof(ranges));
-       if (freq_range_list_parse(&ranges, val) < 0)
+       freqs = freq_range_to_channel_list(wpa_s, val);
+       if (freqs == NULL)
                return -1;
 
-       for (i = 0; i < wpa_s->hw.num_modes; i++) {
-               int j;
-
-               mode = &wpa_s->hw.modes[i];
-               for (j = 0; j < mode->num_channels; j++) {
-                       unsigned int freq;
-
-                       if (mode->channels[j].flag & HOSTAPD_CHAN_DISABLED)
-                               continue;
-
-                       freq = mode->channels[j].freq;
-                       if (!freq_range_list_includes(&ranges, freq))
-                               continue;
-
-                       int_array_add_unique(&freqs, freq);
-               }
-       }
-
-       os_free(ranges.range);
        os_free(wpa_s->manual_scan_freqs);
        wpa_s->manual_scan_freqs = freqs;
 
@@ -5844,9 +5918,11 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
                        reply_len = -1;
        } else if (os_strcmp(buf, "STOP_FETCH_ANQP") == 0) {
                interworking_stop_fetch_anqp(wpa_s);
-       } else if (os_strncmp(buf, "INTERWORKING_SELECT", 19) == 0) {
-               if (interworking_select(wpa_s, os_strstr(buf + 19, "auto") !=
-                                       NULL) < 0)
+       } else if (os_strcmp(buf, "INTERWORKING_SELECT") == 0) {
+               if (ctrl_interworking_select(wpa_s, NULL) < 0)
+                       reply_len = -1;
+       } else if (os_strncmp(buf, "INTERWORKING_SELECT ", 20) == 0) {
+               if (ctrl_interworking_select(wpa_s, buf + 20) < 0)
                        reply_len = -1;
        } else if (os_strncmp(buf, "INTERWORKING_CONNECT ", 21) == 0) {
                if (ctrl_interworking_connect(wpa_s, buf + 21) < 0)
@@ -6272,6 +6348,8 @@ static char * wpas_global_ctrl_iface_redir_p2p(struct wpa_global *global,
 {
 #ifdef CONFIG_P2P
        static const char * cmd[] = {
+               "LIST_NETWORKS",
+               "SAVE_CONFIG",
                "P2P_FIND",
                "P2P_STOP_FIND",
                "P2P_LISTEN",
@@ -6286,6 +6364,12 @@ static char * wpas_global_ctrl_iface_redir_p2p(struct wpa_global *global,
                NULL
        };
        static const char * prefix[] = {
+#ifdef ANDROID
+               "DRIVER ",
+#endif /* ANDROID */
+               "GET_NETWORK ",
+               "REMOVE_NETWORK ",
+               "SET ",
                "P2P_FIND ",
                "P2P_CONNECT ",
                "P2P_LISTEN ",