Clear wpa_supplicant configuration keys explicitly
[mech_eap.git] / wpa_supplicant / wpa_supplicant.c
index b47e423..cf1b283 100644 (file)
@@ -51,6 +51,7 @@
 #include "offchannel.h"
 #include "hs20_supplicant.h"
 #include "wnm_sta.h"
+#include "wpas_kay.h"
 
 const char *wpa_supplicant_version =
 "wpa_supplicant v" VERSION_STR "\n"
@@ -298,6 +299,8 @@ void wpa_supplicant_initiate_eapol(struct wpa_supplicant *wpa_s)
        eapol_conf.external_sim = wpa_s->conf->external_sim;
        eapol_sm_notify_config(wpa_s->eapol, &ssid->eap, &eapol_conf);
 #endif /* IEEE8021X_EAPOL */
+
+       ieee802_1x_alloc_kay_sm(wpa_s, ssid);
 }
 
 
@@ -444,9 +447,7 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
        wpa_supplicant_ap_deinit(wpa_s);
 #endif /* CONFIG_AP */
 
-#ifdef CONFIG_P2P
        wpas_p2p_deinit(wpa_s);
-#endif /* CONFIG_P2P */
 
 #ifdef CONFIG_OFFCHANNEL
        offchannel_deinit(wpa_s);
@@ -468,6 +469,8 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
 
        free_hw_features(wpa_s);
 
+       ieee802_1x_dealloc_kay_sm(wpa_s);
+
        os_free(wpa_s->bssid_filter);
        wpa_s->bssid_filter = NULL;
 
@@ -493,7 +496,7 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
        wpa_s->last_scan_res = NULL;
 
 #ifdef CONFIG_HS20
-       hs20_free_osu_prov(wpa_s);
+       hs20_deinit(wpa_s);
 #endif /* CONFIG_HS20 */
 }
 
@@ -673,8 +676,11 @@ void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s,
                wpa_s->normal_scans = 0;
        }
 
-       if (state == WPA_COMPLETED)
+       if (state == WPA_COMPLETED) {
                wpas_connect_work_done(wpa_s);
+               /* Reinitialize normal_scan counter */
+               wpa_s->normal_scans = 0;
+       }
 
        if (state != WPA_SCANNING)
                wpa_supplicant_notify_scanning(wpa_s, 0);
@@ -697,9 +703,7 @@ void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s,
 #endif /* IEEE8021X_EAPOL */
                wpa_s->after_wps = 0;
                wpa_s->known_wps_freq = 0;
-#ifdef CONFIG_P2P
                wpas_p2p_completed(wpa_s);
-#endif /* CONFIG_P2P */
 
                sme_sched_obss_scan(wpa_s, 1);
        } else if (state == WPA_DISCONNECTED || state == WPA_ASSOCIATING ||
@@ -729,6 +733,12 @@ void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s,
        if (wpa_s->wpa_state != old_state) {
                wpas_notify_state_changed(wpa_s, wpa_s->wpa_state, old_state);
 
+               /*
+                * Notify the P2P Device interface about a state change in one
+                * of the interfaces.
+                */
+               wpas_p2p_indicate_state_change(wpa_s);
+
                if (wpa_s->wpa_state == WPA_COMPLETED ||
                    old_state == WPA_COMPLETED)
                        wpas_notify_auth_changed(wpa_s);
@@ -743,13 +753,13 @@ void wpa_supplicant_terminate_proc(struct wpa_global *global)
        struct wpa_supplicant *wpa_s = global->ifaces;
        while (wpa_s) {
                struct wpa_supplicant *next = wpa_s->next;
+               if (wpas_wps_terminate_pending(wpa_s) == 1)
+                       pending = 1;
 #ifdef CONFIG_P2P
                if (wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE ||
                    (wpa_s->current_ssid && wpa_s->current_ssid->p2p_group))
                        wpas_p2p_disconnect(wpa_s);
 #endif /* CONFIG_P2P */
-               if (wpas_wps_terminate_pending(wpa_s) == 1)
-                       pending = 1;
                wpa_s = next;
        }
 #endif /* CONFIG_WPS */
@@ -976,6 +986,7 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
        } else if (bss_osen && (ssid->proto & WPA_PROTO_OSEN)) {
                wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: using OSEN");
                /* TODO: parse OSEN element */
+               os_memset(&ie, 0, sizeof(ie));
                ie.group_cipher = WPA_CIPHER_CCMP;
                ie.pairwise_cipher = WPA_CIPHER_CCMP;
                ie.key_mgmt = WPA_KEY_MGMT_OSEN;
@@ -1264,13 +1275,18 @@ static void wpas_ext_capab_byte(struct wpa_supplicant *wpa_s, u8 *pos, int idx)
 }
 
 
-int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf)
+int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf, size_t buflen)
 {
        u8 *pos = buf;
        u8 len = 6, i;
 
        if (len < wpa_s->extended_capa_len)
                len = wpa_s->extended_capa_len;
+       if (buflen < (size_t) len + 2) {
+               wpa_printf(MSG_INFO,
+                          "Not enough room for building extended capabilities element");
+               return -1;
+       }
 
        *pos++ = WLAN_EID_EXT_CAPAB;
        *pos++ = len;
@@ -1640,10 +1656,15 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit)
                hs20 = wpabuf_alloc(20);
                if (hs20) {
                        int pps_mo_id = hs20_get_pps_mo_id(wpa_s, ssid);
+                       size_t len;
+
                        wpas_hs20_add_indication(hs20, pps_mo_id);
-                       os_memcpy(wpa_ie + wpa_ie_len, wpabuf_head(hs20),
-                                 wpabuf_len(hs20));
-                       wpa_ie_len += wpabuf_len(hs20);
+                       len = sizeof(wpa_ie) - wpa_ie_len;
+                       if (wpabuf_len(hs20) <= len) {
+                               os_memcpy(wpa_ie + wpa_ie_len,
+                                         wpabuf_head(hs20), wpabuf_len(hs20));
+                               wpa_ie_len += wpabuf_len(hs20);
+                       }
                        wpabuf_free(hs20);
                }
        }
@@ -1658,9 +1679,10 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit)
         * interoperability issues.
         */
        if (!bss || wpa_bss_get_ie(bss, WLAN_EID_EXT_CAPAB)) {
-               u8 ext_capab[10];
+               u8 ext_capab[18];
                int ext_capab_len;
-               ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab);
+               ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab,
+                                                    sizeof(ext_capab));
                if (ext_capab_len > 0) {
                        u8 *pos = wpa_ie;
                        if (wpa_ie_len > 0 && pos[0] == WLAN_EID_RSN)
@@ -1819,9 +1841,11 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit)
         * least prioritized connection.
         */
        if (wpa_s->num_multichan_concurrent < 2) {
-               int freq = wpa_drv_shared_freq(wpa_s);
-               if (freq > 0 && freq != params.freq) {
-                       wpa_printf(MSG_DEBUG, "Shared interface with conflicting frequency found (%d != %d)",
+               int freq, num;
+               num = get_shared_radio_freqs(wpa_s, &freq, 1);
+               if (num > 0 && freq > 0 && freq != params.freq) {
+                       wpa_printf(MSG_DEBUG,
+                                  "Assoc conflicting freq found (%d != %d)",
                                   freq, params.freq);
                        if (wpas_p2p_handle_frequency_conflicts(wpa_s,
                                                                params.freq,
@@ -2922,6 +2946,27 @@ static int wpa_set_disable_sgi(struct wpa_supplicant *wpa_s,
 }
 
 
+static int wpa_set_disable_ldpc(struct wpa_supplicant *wpa_s,
+                              struct ieee80211_ht_capabilities *htcaps,
+                              struct ieee80211_ht_capabilities *htcaps_mask,
+                              int disabled)
+{
+       /* Masking these out disables LDPC */
+       u16 msk = host_to_le16(HT_CAP_INFO_LDPC_CODING_CAP);
+
+       wpa_msg(wpa_s, MSG_DEBUG, "set_disable_ldpc: %d", disabled);
+
+       if (disabled)
+               htcaps->ht_capabilities_info &= ~msk;
+       else
+               htcaps->ht_capabilities_info |= msk;
+
+       htcaps_mask->ht_capabilities_info |= msk;
+
+       return 0;
+}
+
+
 void wpa_supplicant_apply_ht_overrides(
        struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
        struct wpa_driver_associate_params *params)
@@ -2945,6 +2990,13 @@ void wpa_supplicant_apply_ht_overrides(
        wpa_set_ampdu_density(wpa_s, htcaps, htcaps_mask, ssid->ampdu_density);
        wpa_set_disable_ht40(wpa_s, htcaps, htcaps_mask, ssid->disable_ht40);
        wpa_set_disable_sgi(wpa_s, htcaps, htcaps_mask, ssid->disable_sgi);
+       wpa_set_disable_ldpc(wpa_s, htcaps, htcaps_mask, ssid->disable_ldpc);
+
+       if (ssid->ht40_intolerant) {
+               u16 bit = host_to_le16(HT_CAP_INFO_40MHZ_INTOLERANT);
+               htcaps->ht_capabilities_info |= bit;
+               htcaps_mask->ht_capabilities_info |= bit;
+       }
 }
 
 #endif /* CONFIG_HT_OVERRIDES */
@@ -3090,6 +3142,79 @@ int wpas_init_ext_pw(struct wpa_supplicant *wpa_s)
 }
 
 
+static int wpas_check_wowlan_trigger(const char *start, const char *trigger,
+                                    int capa_trigger, u8 *param_trigger)
+{
+       if (os_strcmp(start, trigger) != 0)
+               return 0;
+       if (!capa_trigger)
+               return 0;
+
+       *param_trigger = 1;
+       return 1;
+}
+
+
+int wpas_set_wowlan_triggers(struct wpa_supplicant *wpa_s,
+                            struct wpa_driver_capa *capa)
+{
+       struct wowlan_triggers triggers;
+       char *start, *end, *buf;
+       int last, ret;
+
+       if (!wpa_s->conf->wowlan_triggers)
+               return 0;
+
+       buf = os_strdup(wpa_s->conf->wowlan_triggers);
+       if (buf == NULL)
+               return -1;
+
+       os_memset(&triggers, 0, sizeof(triggers));
+
+#define CHECK_TRIGGER(trigger) \
+       wpas_check_wowlan_trigger(start, #trigger,                      \
+                                 capa->wowlan_triggers.trigger,        \
+                                 &triggers.trigger)
+
+       start = buf;
+       while (*start != '\0') {
+               while (isblank(*start))
+                       start++;
+               if (*start == '\0')
+                       break;
+               end = start;
+               while (!isblank(*end) && *end != '\0')
+                       end++;
+               last = *end == '\0';
+               *end = '\0';
+
+               if (!CHECK_TRIGGER(any) &&
+                   !CHECK_TRIGGER(disconnect) &&
+                   !CHECK_TRIGGER(magic_pkt) &&
+                   !CHECK_TRIGGER(gtk_rekey_failure) &&
+                   !CHECK_TRIGGER(eap_identity_req) &&
+                   !CHECK_TRIGGER(four_way_handshake) &&
+                   !CHECK_TRIGGER(rfkill_release)) {
+                       wpa_printf(MSG_DEBUG,
+                                  "Unknown/unsupported wowlan trigger '%s'",
+                                  start);
+                       ret = -1;
+                       goto out;
+               }
+
+               if (last)
+                       break;
+               start = end + 1;
+       }
+#undef CHECK_TRIGGER
+
+       ret = wpa_drv_wowlan(wpa_s, &triggers);
+out:
+       os_free(buf);
+       return ret;
+}
+
+
 static struct wpa_radio * radio_add_interface(struct wpa_supplicant *wpa_s,
                                              const char *rn)
 {
@@ -3607,16 +3732,22 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s,
                return -1;
        }
 
-#ifdef CONFIG_P2P
        if (iface->p2p_mgmt && wpas_p2p_init(wpa_s->global, wpa_s) < 0) {
                wpa_msg(wpa_s, MSG_ERROR, "Failed to init P2P");
                return -1;
        }
-#endif /* CONFIG_P2P */
 
        if (wpa_bss_init(wpa_s) < 0)
                return -1;
 
+       /*
+        * Set Wake-on-WLAN triggers, if configured.
+        * Note: We don't restore/remove the triggers on shutdown (it doesn't
+        * have effect anyway when the interface is down).
+        */
+       if (wpas_set_wowlan_triggers(wpa_s, &capa) < 0)
+               return -1;
+
 #ifdef CONFIG_EAP_PROXY
 {
        size_t len;
@@ -3655,14 +3786,7 @@ static void wpa_supplicant_deinit_iface(struct wpa_supplicant *wpa_s,
        }
 
        wpa_supplicant_cleanup(wpa_s);
-
-#ifdef CONFIG_P2P
-       if (wpa_s == wpa_s->global->p2p_init_wpa_s && wpa_s->global->p2p) {
-               wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Disable P2P since removing "
-                       "the management interface is being removed");
-               wpas_p2p_deinit_global(wpa_s->global);
-       }
-#endif /* CONFIG_P2P */
+       wpas_p2p_deinit_iface(wpa_s);
 
        wpas_ctrl_radio_work_flush(wpa_s);
        radio_remove_interface(wpa_s);
@@ -4091,11 +4215,7 @@ void wpa_supplicant_update_config(struct wpa_supplicant *wpa_s)
 #ifdef CONFIG_WPS
        wpas_wps_update_config(wpa_s);
 #endif /* CONFIG_WPS */
-
-#ifdef CONFIG_P2P
        wpas_p2p_update_config(wpa_s);
-#endif /* CONFIG_P2P */
-
        wpa_s->conf->changed_parameters = 0;
 }
 
@@ -4212,7 +4332,7 @@ void wpas_connection_failed(struct wpa_supplicant *wpa_s, const u8 *bssid)
        if (count > 3 && wpa_s->current_ssid) {
                wpa_printf(MSG_DEBUG, "Continuous association failures - "
                           "consider temporary network disabling");
-               wpas_auth_failed(wpa_s);
+               wpas_auth_failed(wpa_s, "CONN_FAILED");
        }
 
        switch (count) {
@@ -4275,7 +4395,7 @@ int wpa_supplicant_ctrl_iface_ctrl_rsp_handle(struct wpa_supplicant *wpa_s,
                        wpa_s->reassociate = 1;
                break;
        case WPA_CTRL_REQ_EAP_PASSWORD:
-               os_free(eap->password);
+               bin_clear_free(eap->password, eap->password_len);
                eap->password = (u8 *) os_strdup(value);
                eap->password_len = os_strlen(value);
                eap->pending_req_password = 0;
@@ -4283,7 +4403,7 @@ int wpa_supplicant_ctrl_iface_ctrl_rsp_handle(struct wpa_supplicant *wpa_s,
                        wpa_s->reassociate = 1;
                break;
        case WPA_CTRL_REQ_EAP_NEW_PASSWORD:
-               os_free(eap->new_password);
+               bin_clear_free(eap->new_password, eap->new_password_len);
                eap->new_password = (u8 *) os_strdup(value);
                eap->new_password_len = os_strlen(value);
                eap->pending_req_new_password = 0;
@@ -4291,14 +4411,14 @@ int wpa_supplicant_ctrl_iface_ctrl_rsp_handle(struct wpa_supplicant *wpa_s,
                        wpa_s->reassociate = 1;
                break;
        case WPA_CTRL_REQ_EAP_PIN:
-               os_free(eap->pin);
+               str_clear_free(eap->pin);
                eap->pin = os_strdup(value);
                eap->pending_req_pin = 0;
                if (ssid == wpa_s->current_ssid)
                        wpa_s->reassociate = 1;
                break;
        case WPA_CTRL_REQ_EAP_OTP:
-               os_free(eap->otp);
+               bin_clear_free(eap->otp, eap->otp_len);
                eap->otp = (u8 *) os_strdup(value);
                eap->otp_len = os_strlen(value);
                os_free(eap->pending_req_otp);
@@ -4306,14 +4426,14 @@ int wpa_supplicant_ctrl_iface_ctrl_rsp_handle(struct wpa_supplicant *wpa_s,
                eap->pending_req_otp_len = 0;
                break;
        case WPA_CTRL_REQ_EAP_PASSPHRASE:
-               os_free(eap->private_key_passwd);
-               eap->private_key_passwd = (u8 *) os_strdup(value);
+               str_clear_free(eap->private_key_passwd);
+               eap->private_key_passwd = os_strdup(value);
                eap->pending_req_passphrase = 0;
                if (ssid == wpa_s->current_ssid)
                        wpa_s->reassociate = 1;
                break;
        case WPA_CTRL_REQ_SIM:
-               os_free(eap->external_sim_resp);
+               str_clear_free(eap->external_sim_resp);
                eap->external_sim_resp = os_strdup(value);
                break;
        default:
@@ -4360,7 +4480,7 @@ int wpas_network_disabled(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
        }
 
        if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt) && !ssid->psk_set &&
-           !ssid->ext_psk)
+           (!ssid->passphrase || ssid->ssid_len != 0) && !ssid->ext_psk)
                return 1;
 
        return 0;
@@ -4377,7 +4497,7 @@ int wpas_is_p2p_prioritized(struct wpa_supplicant *wpa_s)
 }
 
 
-void wpas_auth_failed(struct wpa_supplicant *wpa_s)
+void wpas_auth_failed(struct wpa_supplicant *wpa_s, char *reason)
 {
        struct wpa_ssid *ssid = wpa_s->current_ssid;
        int dur;
@@ -4431,9 +4551,9 @@ void wpas_auth_failed(struct wpa_supplicant *wpa_s)
        ssid->disabled_until.sec = now.sec + dur;
 
        wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_TEMP_DISABLED
-               "id=%d ssid=\"%s\" auth_failures=%u duration=%d",
+               "id=%d ssid=\"%s\" auth_failures=%u duration=%d reason=%s",
                ssid->id, wpa_ssid_txt(ssid->ssid, ssid->ssid_len),
-               ssid->auth_failures, dur);
+               ssid->auth_failures, dur, reason);
 }
 
 
@@ -4512,24 +4632,30 @@ void wpas_request_connection(struct wpa_supplicant *wpa_s)
 }
 
 
-void dump_freq_array(struct wpa_supplicant *wpa_s, const char *title,
-                    int *freq_array, unsigned int len)
+void dump_freq_data(struct wpa_supplicant *wpa_s, const char *title,
+                   struct wpa_used_freq_data *freqs_data,
+                   unsigned int len)
 {
        unsigned int i;
 
        wpa_dbg(wpa_s, MSG_DEBUG, "Shared frequencies (len=%u): %s",
                len, title);
-       for (i = 0; i < len; i++)
-               wpa_dbg(wpa_s, MSG_DEBUG, "freq[%u]: %d", i, freq_array[i]);
+       for (i = 0; i < len; i++) {
+               struct wpa_used_freq_data *cur = &freqs_data[i];
+               wpa_dbg(wpa_s, MSG_DEBUG, "freq[%u]: %d, flags=0x%X",
+                       i, cur->freq, cur->flags);
+       }
 }
 
 
 /*
  * Find the operating frequencies of any of the virtual interfaces that
- * are using the same radio as the current interface.
+ * are using the same radio as the current interface, and in addition, get
+ * information about the interface types that are using the frequency.
  */
-int get_shared_radio_freqs(struct wpa_supplicant *wpa_s,
-                          int *freq_array, unsigned int len)
+int get_shared_radio_freqs_data(struct wpa_supplicant *wpa_s,
+                               struct wpa_used_freq_data *freqs_data,
+                               unsigned int len)
 {
        struct wpa_supplicant *ifs;
        u8 bssid[ETH_ALEN];
@@ -4538,31 +4664,12 @@ int get_shared_radio_freqs(struct wpa_supplicant *wpa_s,
 
        wpa_dbg(wpa_s, MSG_DEBUG,
                "Determining shared radio frequencies (max len %u)", len);
-       os_memset(freq_array, 0, sizeof(int) * len);
-
-       /* First add the frequency of the local interface */
-       if (wpa_s->current_ssid != NULL && wpa_s->assoc_freq != 0) {
-               if (wpa_s->current_ssid->mode == WPAS_MODE_AP ||
-                   wpa_s->current_ssid->mode == WPAS_MODE_P2P_GO)
-                       freq_array[idx++] = wpa_s->current_ssid->frequency;
-               else if (wpa_drv_get_bssid(wpa_s, bssid) == 0)
-                       freq_array[idx++] = wpa_s->assoc_freq;
-       }
-
-       /* If get_radio_name is not supported, use only the local freq */
-       if (!wpa_driver_get_radio_name(wpa_s)) {
-               freq = wpa_drv_shared_freq(wpa_s);
-               if (freq > 0 && idx < len &&
-                   (idx == 0 || freq_array[0] != freq))
-                       freq_array[idx++] = freq;
-               dump_freq_array(wpa_s, "No get_radio_name", freq_array, idx);
-               return idx;
-       }
+       os_memset(freqs_data, 0, sizeof(struct wpa_used_freq_data) * len);
 
        dl_list_for_each(ifs, &wpa_s->radio->ifaces, struct wpa_supplicant,
                         radio_list) {
-               if (wpa_s == ifs)
-                       continue;
+               if (idx == len)
+                       break;
 
                if (ifs->current_ssid == NULL || ifs->assoc_freq == 0)
                        continue;
@@ -4577,13 +4684,45 @@ int get_shared_radio_freqs(struct wpa_supplicant *wpa_s,
 
                /* Hold only distinct freqs */
                for (i = 0; i < idx; i++)
-                       if (freq_array[i] == freq)
+                       if (freqs_data[i].freq == freq)
                                break;
 
                if (i == idx)
-                       freq_array[idx++] = freq;
+                       freqs_data[idx++].freq = freq;
+
+               if (ifs->current_ssid->mode == WPAS_MODE_INFRA) {
+                       freqs_data[i].flags = ifs->current_ssid->p2p_group ?
+                               WPA_FREQ_USED_BY_P2P_CLIENT :
+                               WPA_FREQ_USED_BY_INFRA_STATION;
+               }
        }
 
-       dump_freq_array(wpa_s, "completed iteration", freq_array, idx);
+       dump_freq_data(wpa_s, "completed iteration", freqs_data, idx);
        return idx;
 }
+
+
+/*
+ * Find the operating frequencies of any of the virtual interfaces that
+ * are using the same radio as the current interface.
+ */
+int get_shared_radio_freqs(struct wpa_supplicant *wpa_s,
+                          int *freq_array, unsigned int len)
+{
+       struct wpa_used_freq_data *freqs_data;
+       int num, i;
+
+       os_memset(freq_array, 0, sizeof(int) * len);
+
+       freqs_data = os_calloc(len, sizeof(struct wpa_used_freq_data));
+       if (!freqs_data)
+               return -1;
+
+       num = get_shared_radio_freqs_data(wpa_s, freqs_data, len);
+       for (i = 0; i < num; i++)
+               freq_array[i] = freqs_data[i].freq;
+
+       os_free(freqs_data);
+
+       return num;
+}