From 2b0ba7bae5b60321ee7843871a2cf15ad6b3f65b Mon Sep 17 00:00:00 2001 From: sam Date: Sun, 5 Jun 2005 20:52:14 +0000 Subject: Stripped down import of wpa_supplicant v0.3.8 --- contrib/wpa_supplicant/wpa_supplicant.c | 2439 +++++++++++++++++++++++++++++++ 1 file changed, 2439 insertions(+) create mode 100644 contrib/wpa_supplicant/wpa_supplicant.c (limited to 'contrib/wpa_supplicant/wpa_supplicant.c') diff --git a/contrib/wpa_supplicant/wpa_supplicant.c b/contrib/wpa_supplicant/wpa_supplicant.c new file mode 100644 index 0000000..10f1212 --- /dev/null +++ b/contrib/wpa_supplicant/wpa_supplicant.c @@ -0,0 +1,2439 @@ +/* + * WPA Supplicant + * Copyright (c) 2003-2005, Jouni Malinen + * + * 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 + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef CONFIG_NATIVE_WINDOWS +#include +#include +#endif /* CONFIG_NATIVE_WINDOWS */ +#include +#include +#ifndef CONFIG_NATIVE_WINDOWS +#include +#endif /* CONFIG_NATIVE_WINDOWS */ +#include + +#define OPENSSL_DISABLE_OLD_DES_SUPPORT +#include "common.h" +#include "eapol_sm.h" +#include "eap.h" +#include "wpa.h" +#include "driver.h" +#include "eloop.h" +#include "wpa_supplicant.h" +#include "config.h" +#include "l2_packet.h" +#include "wpa_supplicant_i.h" +#include "ctrl_iface.h" +#include "pcsc_funcs.h" +#include "version.h" + +static const char *wpa_supplicant_version = +"wpa_supplicant v" VERSION_STR "\n" +"Copyright (c) 2003-2005, Jouni Malinen and contributors"; + +static const char *wpa_supplicant_license = +"This program is free software. You can distribute it and/or modify it\n" +"under the terms of the GNU General Public License version 2.\n" +"\n" +"Alternatively, this software may be distributed under the terms of the\n" +"BSD license. See README and COPYING for more details.\n" +#ifdef EAP_TLS_FUNCS +"\nThis product includes software developed by the OpenSSL Project\n" +"for use in the OpenSSL Toolkit (http://www.openssl.org/)\n" +#endif /* EAP_TLS_FUNCS */ +; + +static const char *wpa_supplicant_full_license = +"This program is free software; you can redistribute it and/or modify\n" +"it under the terms of the GNU General Public License version 2 as\n" +"published by the Free Software Foundation.\n" +"\n" +"This program is distributed in the hope that it will be useful,\n" +"but WITHOUT ANY WARRANTY; without even the implied warranty of\n" +"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" +"GNU General Public License for more details.\n" +"\n" +"You should have received a copy of the GNU General Public License\n" +"along with this program; if not, write to the Free Software\n" +"Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\n" +"\n" +"Alternatively, this software may be distributed under the terms of the\n" +"BSD license.\n" +"\n" +"Redistribution and use in source and binary forms, with or without\n" +"modification, are permitted provided that the following conditions are\n" +"met:\n" +"\n" +"1. Redistributions of source code must retain the above copyright\n" +" notice, this list of conditions and the following disclaimer.\n" +"\n" +"2. Redistributions in binary form must reproduce the above copyright\n" +" notice, this list of conditions and the following disclaimer in the\n" +" documentation and/or other materials provided with the distribution.\n" +"\n" +"3. Neither the name(s) of the above-listed copyright holder(s) nor the\n" +" names of its contributors may be used to endorse or promote products\n" +" derived from this software without specific prior written permission.\n" +"\n" +"THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n" +"\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n" +"LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n" +"A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n" +"OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n" +"SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n" +"LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n" +"DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n" +"THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n" +"(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n" +"OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n" +"\n"; + +extern struct wpa_driver_ops *wpa_supplicant_drivers[]; + +static void wpa_supplicant_scan_results(struct wpa_supplicant *wpa_s); +static int wpa_supplicant_driver_init(struct wpa_supplicant *wpa_s, + int wait_for_interface); +static void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, + struct wpa_scan_result *bss, + struct wpa_ssid *ssid); +static int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, + struct wpa_scan_result *bss, + struct wpa_ssid *ssid, + u8 *wpa_ie, int *wpa_ie_len); + + +extern int wpa_debug_level; +extern int wpa_debug_show_keys; +extern int wpa_debug_timestamp; + + +void wpa_msg(struct wpa_supplicant *wpa_s, int level, char *fmt, ...) +{ + va_list ap; + char *buf; + const int buflen = 2048; + int len; + + buf = malloc(buflen); + if (buf == NULL) { + printf("Failed to allocate message buffer for:\n"); + va_start(ap, fmt); + vprintf(fmt, ap); + printf("\n"); + va_end(ap); + return; + } + va_start(ap, fmt); + len = vsnprintf(buf, buflen, fmt, ap); + va_end(ap); + wpa_printf(level, "%s", buf); + wpa_supplicant_ctrl_iface_send(wpa_s, level, buf, len); + free(buf); +} + + +int wpa_eapol_send(void *ctx, int type, u8 *buf, size_t len) +{ + struct wpa_supplicant *wpa_s = ctx; + u8 *msg, *dst, bssid[ETH_ALEN]; + size_t msglen; + struct l2_ethhdr *ethhdr; + struct ieee802_1x_hdr *hdr; + int res; + + /* TODO: could add l2_packet_sendmsg that allows fragments to avoid + * extra copy here */ + + if (wpa_s->key_mgmt == WPA_KEY_MGMT_PSK || + wpa_s->key_mgmt == WPA_KEY_MGMT_NONE) { + /* Current SSID is not using IEEE 802.1X/EAP, so drop possible + * EAPOL frames (mainly, EAPOL-Start) from EAPOL state + * machines. */ + wpa_printf(MSG_DEBUG, "WPA: drop TX EAPOL in non-IEEE 802.1X " + "mode (type=%d len=%lu)", type, + (unsigned long) len); + return -1; + } + + if (wpa_s->cur_pmksa && type == IEEE802_1X_TYPE_EAPOL_START) { + /* Trying to use PMKSA caching - do not send EAPOL-Start frames + * since they will trigger full EAPOL authentication. */ + wpa_printf(MSG_DEBUG, "RSN: PMKSA caching - do not send " + "EAPOL-Start"); + return -1; + } + + if (memcmp(wpa_s->bssid, "\x00\x00\x00\x00\x00\x00", ETH_ALEN) == 0) { + wpa_printf(MSG_DEBUG, "BSSID not set when trying to send an " + "EAPOL frame"); + if (wpa_drv_get_bssid(wpa_s, bssid) == 0 && + memcmp(bssid, "\x00\x00\x00\x00\x00\x00", ETH_ALEN) != 0) { + dst = bssid; + wpa_printf(MSG_DEBUG, "Using current BSSID " MACSTR + " from the driver as the EAPOL destination", + MAC2STR(dst)); + } else { + dst = wpa_s->last_eapol_src; + wpa_printf(MSG_DEBUG, "Using the source address of the" + " last received EAPOL frame " MACSTR " as " + "the EAPOL destination", + MAC2STR(dst)); + } + } else { + /* BSSID was already set (from (Re)Assoc event, so use it as + * the EAPOL destination. */ + dst = wpa_s->bssid; + } + + msglen = sizeof(*ethhdr) + sizeof(*hdr) + len; + msg = malloc(msglen); + if (msg == NULL) + return -1; + + ethhdr = (struct l2_ethhdr *) msg; + memcpy(ethhdr->h_dest, dst, ETH_ALEN); + memcpy(ethhdr->h_source, wpa_s->own_addr, ETH_ALEN); + ethhdr->h_proto = htons(ETH_P_EAPOL); + + hdr = (struct ieee802_1x_hdr *) (ethhdr + 1); + hdr->version = wpa_s->conf->eapol_version; + hdr->type = type; + hdr->length = htons(len); + + memcpy((u8 *) (hdr + 1), buf, len); + + wpa_hexdump(MSG_MSGDUMP, "TX EAPOL", msg, msglen); + res = l2_packet_send(wpa_s->l2, msg, msglen); + free(msg); + return res; +} + + +int wpa_eapol_send_preauth(void *ctx, int type, u8 *buf, size_t len) +{ + struct wpa_supplicant *wpa_s = ctx; + u8 *msg; + size_t msglen; + struct l2_ethhdr *ethhdr; + struct ieee802_1x_hdr *hdr; + int res; + + /* TODO: could add l2_packet_sendmsg that allows fragments to avoid + * extra copy here */ + + if (wpa_s->l2_preauth == NULL) + return -1; + + msglen = sizeof(*ethhdr) + sizeof(*hdr) + len; + msg = malloc(msglen); + if (msg == NULL) + return -1; + + ethhdr = (struct l2_ethhdr *) msg; + memcpy(ethhdr->h_dest, wpa_s->preauth_bssid, ETH_ALEN); + memcpy(ethhdr->h_source, wpa_s->own_addr, ETH_ALEN); + ethhdr->h_proto = htons(ETH_P_RSN_PREAUTH); + + hdr = (struct ieee802_1x_hdr *) (ethhdr + 1); + hdr->version = wpa_s->conf->eapol_version; + hdr->type = type; + hdr->length = htons(len); + + memcpy((u8 *) (hdr + 1), buf, len); + + wpa_hexdump(MSG_MSGDUMP, "TX EAPOL (preauth)", msg, msglen); + res = l2_packet_send(wpa_s->l2_preauth, msg, msglen); + free(msg); + return res; +} + + +/** + * wpa_eapol_set_wep_key - set WEP key for the driver + * @ctx: pointer to wpa_supplicant data + * @unicast: 1 = individual unicast key, 0 = broadcast key + * @keyidx: WEP key index (0..3) + * @key: pointer to key data + * @keylen: key length in bytes + * + * Returns 0 on success or < 0 on error. + */ +static int wpa_eapol_set_wep_key(void *ctx, int unicast, int keyidx, + u8 *key, size_t keylen) +{ + struct wpa_supplicant *wpa_s = ctx; + wpa_s->keys_cleared = 0; + return wpa_drv_set_key(wpa_s, WPA_ALG_WEP, + unicast ? wpa_s->bssid : + (u8 *) "\xff\xff\xff\xff\xff\xff", + keyidx, unicast, (u8 *) "", 0, key, keylen); +} + + +/* Configure default/group WEP key for static WEP */ +static int wpa_set_wep_key(void *ctx, int set_tx, int keyidx, const u8 *key, + size_t keylen) +{ + struct wpa_supplicant *wpa_s = ctx; + wpa_s->keys_cleared = 0; + return wpa_drv_set_key(wpa_s, WPA_ALG_WEP, + (u8 *) "\xff\xff\xff\xff\xff\xff", + keyidx, set_tx, (u8 *) "", 0, key, keylen); +} + + +static int wpa_supplicant_set_wpa_none_key(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ + u8 key[32]; + size_t keylen; + wpa_alg alg; + u8 seq[6] = { 0 }; + + /* IBSS/WPA-None uses only one key (Group) for both receiving and + * sending unicast and multicast packets. */ + + if (ssid->mode != IEEE80211_MODE_IBSS) { + wpa_printf(MSG_INFO, "WPA: Invalid mode %d (not IBSS/ad-hoc) " + "for WPA-None", ssid->mode); + return -1; + } + + if (!ssid->psk_set) { + wpa_printf(MSG_INFO, "WPA: No PSK configured for WPA-None"); + return -1; + } + + switch (wpa_s->group_cipher) { + case WPA_CIPHER_CCMP: + memcpy(key, ssid->psk, 16); + keylen = 16; + alg = WPA_ALG_CCMP; + break; + case WPA_CIPHER_TKIP: + /* WPA-None uses the same Michael MIC key for both TX and RX */ + memcpy(key, ssid->psk, 16 + 8); + memcpy(key + 16 + 8, ssid->psk + 16, 8); + keylen = 32; + alg = WPA_ALG_TKIP; + break; + default: + wpa_printf(MSG_INFO, "WPA: Invalid group cipher %d for " + "WPA-None", wpa_s->group_cipher); + return -1; + } + + /* TODO: should actually remember the previously used seq#, both for TX + * and RX from each STA.. */ + + return wpa_drv_set_key(wpa_s, alg, (u8 *) "\xff\xff\xff\xff\xff\xff", + 0, 1, seq, 6, key, keylen); +} + + +void wpa_supplicant_notify_eapol_done(void *ctx) +{ + struct wpa_supplicant *wpa_s = ctx; + wpa_msg(wpa_s, MSG_DEBUG, "WPA: EAPOL processing complete"); + eloop_cancel_timeout(wpa_supplicant_scan, wpa_s, NULL); + wpa_supplicant_cancel_auth_timeout(wpa_s); +} + + +static int wpa_blacklisted(struct wpa_supplicant *wpa_s, const u8 *bssid) +{ + struct wpa_blacklist *e; + + e = wpa_s->blacklist; + while (e) { + if (memcmp(e->bssid, bssid, ETH_ALEN) == 0) + return 1; + e = e->next; + } + + return 0; +} + + +static int wpa_blacklist_add(struct wpa_supplicant *wpa_s, const u8 *bssid) +{ + struct wpa_blacklist *e; + + if (wpa_blacklisted(wpa_s, bssid)) + return 0; + + e = malloc(sizeof(*e)); + if (e == NULL) + return -1; + memset(e, 0, sizeof(*e)); + memcpy(e->bssid, bssid, ETH_ALEN); + e->next = wpa_s->blacklist; + wpa_s->blacklist = e; + wpa_printf(MSG_DEBUG, "Added BSSID " MACSTR " into blacklist", + MAC2STR(bssid)); + + return 0; +} + + +static int wpa_blacklist_del(struct wpa_supplicant *wpa_s, const u8 *bssid) +{ + struct wpa_blacklist *e, *prev = NULL; + + e = wpa_s->blacklist; + while (e) { + if (memcmp(e->bssid, bssid, ETH_ALEN) == 0) { + if (prev == NULL) { + wpa_s->blacklist = e->next; + } else { + prev->next = e->next; + } + wpa_printf(MSG_DEBUG, "Removed BSSID " MACSTR " from " + "blacklist", MAC2STR(bssid)); + free(e); + return 0; + } + prev = e; + e = e->next; + } + return -1; +} + + +static void wpa_blacklist_clear(struct wpa_supplicant *wpa_s) +{ + struct wpa_blacklist *e, *prev; + + e = wpa_s->blacklist; + wpa_s->blacklist = NULL; + while (e) { + prev = e; + e = e->next; + wpa_printf(MSG_DEBUG, "Removed BSSID " MACSTR " from " + "blacklist (clear)", MAC2STR(prev->bssid)); + free(prev); + } +} + + +const char * wpa_ssid_txt(u8 *ssid, size_t ssid_len) +{ + static char ssid_txt[MAX_SSID_LEN + 1]; + char *pos; + + if (ssid_len > MAX_SSID_LEN) + ssid_len = MAX_SSID_LEN; + memcpy(ssid_txt, ssid, ssid_len); + ssid_txt[ssid_len] = '\0'; + for (pos = ssid_txt; *pos != '\0'; pos++) { + if ((u8) *pos < 32 || (u8) *pos >= 127) + *pos = '_'; + } + return ssid_txt; +} + + +void wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec) +{ + wpa_msg(wpa_s, MSG_DEBUG, "Setting scan request: %d sec %d usec", + sec, usec); + eloop_cancel_timeout(wpa_supplicant_scan, wpa_s, NULL); + eloop_register_timeout(sec, usec, wpa_supplicant_scan, wpa_s, NULL); +} + + +void wpa_supplicant_cancel_scan(struct wpa_supplicant *wpa_s) +{ + wpa_msg(wpa_s, MSG_DEBUG, "Cancelling scan request"); + eloop_cancel_timeout(wpa_supplicant_scan, wpa_s, NULL); +} + + +static void wpa_supplicant_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + wpa_msg(wpa_s, MSG_INFO, "Authentication with " MACSTR " timed out.", + MAC2STR(wpa_s->bssid)); + wpa_blacklist_add(wpa_s, wpa_s->bssid); + wpa_supplicant_disassociate(wpa_s, REASON_DEAUTH_LEAVING); + wpa_s->reassociate = 1; + wpa_supplicant_req_scan(wpa_s, 0, 0); +} + + +void wpa_supplicant_req_auth_timeout(struct wpa_supplicant *wpa_s, + int sec, int usec) +{ + wpa_msg(wpa_s, MSG_DEBUG, "Setting authentication timeout: %d sec " + "%d usec", sec, usec); + eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL); + eloop_register_timeout(sec, usec, wpa_supplicant_timeout, wpa_s, NULL); +} + + +void wpa_supplicant_cancel_auth_timeout(struct wpa_supplicant *wpa_s) +{ + wpa_msg(wpa_s, MSG_DEBUG, "Cancelling authentication timeout"); + eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL); + wpa_blacklist_del(wpa_s, wpa_s->bssid); +} + + +static void wpa_supplicant_initiate_eapol(struct wpa_supplicant *wpa_s) +{ + struct eapol_config eapol_conf; + struct wpa_ssid *ssid = wpa_s->current_ssid; + + if (wpa_s->key_mgmt == WPA_KEY_MGMT_PSK) { + eapol_sm_notify_eap_success(wpa_s->eapol, FALSE); + eapol_sm_notify_eap_fail(wpa_s->eapol, FALSE); + } + if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE || + wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) + eapol_sm_notify_portControl(wpa_s->eapol, ForceAuthorized); + else + eapol_sm_notify_portControl(wpa_s->eapol, Auto); + + memset(&eapol_conf, 0, sizeof(eapol_conf)); + if (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) { + eapol_conf.accept_802_1x_keys = 1; + eapol_conf.required_keys = 0; + if (ssid->eapol_flags & EAPOL_FLAG_REQUIRE_KEY_UNICAST) { + eapol_conf.required_keys |= EAPOL_REQUIRE_KEY_UNICAST; + } + if (ssid->eapol_flags & EAPOL_FLAG_REQUIRE_KEY_BROADCAST) { + eapol_conf.required_keys |= + EAPOL_REQUIRE_KEY_BROADCAST; + } + } + eapol_conf.fast_reauth = wpa_s->conf->fast_reauth; + eapol_conf.workaround = ssid->eap_workaround; + eapol_sm_notify_config(wpa_s->eapol, ssid, &eapol_conf); +} + + +static void wpa_supplicant_set_non_wpa_policy(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ + int i; + + if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) + wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X_NO_WPA; + else + wpa_s->key_mgmt = WPA_KEY_MGMT_NONE; + free(wpa_s->ap_wpa_ie); + wpa_s->ap_wpa_ie = NULL; + wpa_s->ap_wpa_ie_len = 0; + free(wpa_s->ap_rsn_ie); + wpa_s->ap_rsn_ie = NULL; + wpa_s->ap_rsn_ie_len = 0; + free(wpa_s->assoc_wpa_ie); + wpa_s->assoc_wpa_ie = NULL; + wpa_s->assoc_wpa_ie_len = 0; + wpa_s->pairwise_cipher = WPA_CIPHER_NONE; + wpa_s->group_cipher = WPA_CIPHER_NONE; + + for (i = 0; i < NUM_WEP_KEYS; i++) { + if (ssid->wep_key_len[i] > 5) { + wpa_s->pairwise_cipher = WPA_CIPHER_WEP104; + wpa_s->group_cipher = WPA_CIPHER_WEP104; + break; + } else if (ssid->wep_key_len[i] > 0) { + wpa_s->pairwise_cipher = WPA_CIPHER_WEP40; + wpa_s->group_cipher = WPA_CIPHER_WEP40; + break; + } + } + + wpa_s->cur_pmksa = NULL; +} + + +static int wpa_supplicant_select_config(struct wpa_supplicant *wpa_s) +{ + struct wpa_ssid *ssid; + + if (wpa_s->conf->ap_scan == 1) + return 0; + + ssid = wpa_supplicant_get_ssid(wpa_s); + if (ssid == NULL) { + wpa_printf(MSG_INFO, "No network configuration found for the " + "current AP"); + return -1; + } + + wpa_printf(MSG_DEBUG, "Network configuration found for the current " + "AP"); + if (ssid->key_mgmt & (WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_IEEE8021X | + WPA_KEY_MGMT_WPA_NONE)) { + u8 wpa_ie[80]; + int wpa_ie_len; + wpa_supplicant_set_suites(wpa_s, NULL, ssid, + wpa_ie, &wpa_ie_len); + } else { + wpa_supplicant_set_non_wpa_policy(wpa_s, ssid); + } + + wpa_s->current_ssid = ssid; + wpa_supplicant_initiate_eapol(wpa_s); + + return 0; +} + + +static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) +{ + scard_deinit(wpa_s->scard); + wpa_s->scard = NULL; + eapol_sm_register_scard_ctx(wpa_s->eapol, NULL); + l2_packet_deinit(wpa_s->l2); + wpa_s->l2 = NULL; + +#ifdef CONFIG_XSUPPLICANT_IFACE + if (wpa_s->dot1x_s > -1) { + close(wpa_s->dot1x_s); + wpa_s->dot1x_s = -1; + } +#endif /* CONFIG_XSUPPLICANT_IFACE */ + + wpa_supplicant_ctrl_iface_deinit(wpa_s); + if (wpa_s->conf != NULL) { + wpa_config_free(wpa_s->conf); + wpa_s->conf = NULL; + } + + free(wpa_s->assoc_wpa_ie); + wpa_s->assoc_wpa_ie = NULL; + + free(wpa_s->ap_wpa_ie); + wpa_s->ap_wpa_ie = NULL; + free(wpa_s->ap_rsn_ie); + wpa_s->ap_rsn_ie = NULL; + + free(wpa_s->confname); + wpa_s->confname = NULL; + + eapol_sm_deinit(wpa_s->eapol); + wpa_s->eapol = NULL; + + rsn_preauth_deinit(wpa_s); + + pmksa_candidate_free(wpa_s); + pmksa_cache_free(wpa_s); + wpa_blacklist_clear(wpa_s); + + free(wpa_s->scan_results); + wpa_s->scan_results = NULL; + wpa_s->num_scan_results = 0; +} + + +static void wpa_clear_keys(struct wpa_supplicant *wpa_s, u8 *addr) +{ + u8 *bcast = (u8 *) "\xff\xff\xff\xff\xff\xff"; + + if (wpa_s->keys_cleared) { + /* Some drivers (e.g., ndiswrapper & NDIS drivers) seem to have + * timing issues with keys being cleared just before new keys + * are set or just after association or something similar. This + * shows up in group key handshake failing often because of the + * client not receiving the first encrypted packets correctly. + * Skipping some of the extra key clearing steps seems to help + * in completing group key handshake more reliably. */ + wpa_printf(MSG_DEBUG, "No keys have been configured - " + "skip key clearing"); + return; + } + + wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 0, 0, NULL, 0, NULL, 0); + wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 1, 0, NULL, 0, NULL, 0); + wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 2, 0, NULL, 0, NULL, 0); + wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 3, 0, NULL, 0, NULL, 0); + if (addr) { + wpa_drv_set_key(wpa_s, WPA_ALG_NONE, addr, 0, 0, NULL, 0, NULL, + 0); + } + wpa_s->keys_cleared = 1; +} + + +static void wpa_supplicant_stop_countermeasures(void *eloop_ctx, + void *sock_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + + if (wpa_s->countermeasures) { + wpa_s->countermeasures = 0; + wpa_drv_set_countermeasures(wpa_s, 0); + wpa_msg(wpa_s, MSG_INFO, "WPA: TKIP countermeasures stopped"); + wpa_supplicant_req_scan(wpa_s, 0, 0); + } +} + + +static void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s) +{ + wpa_s->wpa_state = WPA_DISCONNECTED; + memset(wpa_s->bssid, 0, ETH_ALEN); + eapol_sm_notify_portEnabled(wpa_s->eapol, FALSE); + eapol_sm_notify_portValid(wpa_s->eapol, FALSE); + if (wpa_s->key_mgmt == WPA_KEY_MGMT_PSK) + eapol_sm_notify_eap_success(wpa_s->eapol, FALSE); +} + + +static void wpa_find_assoc_pmkid(struct wpa_supplicant *wpa_s) +{ + struct wpa_ie_data ie; + int i; + + if (wpa_parse_wpa_ie(wpa_s, wpa_s->assoc_wpa_ie, + wpa_s->assoc_wpa_ie_len, &ie) < 0 || + ie.pmkid == NULL) + return; + + for (i = 0; i < ie.num_pmkid; i++) { + wpa_s->cur_pmksa = pmksa_cache_get(wpa_s, NULL, + ie.pmkid + i * PMKID_LEN); + if (wpa_s->cur_pmksa) { + eapol_sm_notify_pmkid_attempt(wpa_s->eapol, 1); + break; + } + } + + wpa_printf(MSG_DEBUG, "RSN: PMKID from assoc IE %sfound from PMKSA " + "cache", wpa_s->cur_pmksa ? "" : "not "); +} + + +static void wpa_supplicant_add_pmkid_candidate(struct wpa_supplicant *wpa_s, + union wpa_event_data *data) +{ + if (data == NULL) { + wpa_printf(MSG_DEBUG, "RSN: No data in PMKID candidate event"); + return; + } + wpa_printf(MSG_DEBUG, "RSN: PMKID candidate event - bssid=" MACSTR + " index=%d preauth=%d", + MAC2STR(data->pmkid_candidate.bssid), + data->pmkid_candidate.index, + data->pmkid_candidate.preauth); + + if (!data->pmkid_candidate.preauth) { + wpa_printf(MSG_DEBUG, "RSN: Ignored PMKID candidate without " + "preauth flag"); + return; + } + + pmksa_candidate_add(wpa_s, data->pmkid_candidate.bssid, + data->pmkid_candidate.index); +} + + +static int wpa_supplicant_dynamic_keys(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE || + wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) + return 0; + + if (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA && + wpa_s->current_ssid && + !(wpa_s->current_ssid->eapol_flags & + (EAPOL_FLAG_REQUIRE_KEY_UNICAST | + EAPOL_FLAG_REQUIRE_KEY_BROADCAST))) { + /* IEEE 802.1X, but not using dynamic WEP keys (i.e., either + * plaintext or static WEP keys). */ + return 0; + } + + return 1; +} + + +static void wpa_supplicant_associnfo(struct wpa_supplicant *wpa_s, + union wpa_event_data *data) +{ + int l, len; + u8 *p; + + wpa_printf(MSG_DEBUG, "Association info event"); + wpa_hexdump(MSG_DEBUG, "req_ies", data->assoc_info.req_ies, + data->assoc_info.req_ies_len); + wpa_hexdump(MSG_DEBUG, "resp_ies", data->assoc_info.resp_ies, + data->assoc_info.resp_ies_len); + if (wpa_s->assoc_wpa_ie) { + free(wpa_s->assoc_wpa_ie); + wpa_s->assoc_wpa_ie = NULL; + wpa_s->assoc_wpa_ie_len = 0; + } + + p = data->assoc_info.req_ies; + l = data->assoc_info.req_ies_len; + + /* Go through the IEs and make a copy of the WPA/RSN IE, if present. */ + while (l >= 2) { + len = p[1] + 2; + if (len > l) { + wpa_hexdump(MSG_DEBUG, "Truncated IE in assoc_info", + p, l); + break; + } + if ((p[0] == GENERIC_INFO_ELEM && p[1] >= 6 && + (memcmp(&p[2], "\x00\x50\xF2\x01\x01\x00", 6) == 0)) || + (p[0] == RSN_INFO_ELEM && p[1] >= 2)) { + wpa_s->assoc_wpa_ie = malloc(len); + if (wpa_s->assoc_wpa_ie == NULL) + break; + wpa_s->assoc_wpa_ie_len = len; + memcpy(wpa_s->assoc_wpa_ie, p, len); + wpa_hexdump(MSG_DEBUG, "assoc_wpa_ie", + wpa_s->assoc_wpa_ie, + wpa_s->assoc_wpa_ie_len); + wpa_find_assoc_pmkid(wpa_s); + break; + } + l -= len; + p += len; + } + + /* WPA/RSN IE from Beacon/ProbeResp */ + free(wpa_s->ap_wpa_ie); + wpa_s->ap_wpa_ie = NULL; + wpa_s->ap_wpa_ie_len = 0; + free(wpa_s->ap_rsn_ie); + wpa_s->ap_rsn_ie = NULL; + wpa_s->ap_rsn_ie_len = 0; + + p = data->assoc_info.beacon_ies; + l = data->assoc_info.beacon_ies_len; + + /* Go through the IEs and make a copy of the WPA/RSN IEs, if present. + */ + while (l >= 2) { + len = p[1] + 2; + if (len > l) { + wpa_hexdump(MSG_DEBUG, "Truncated IE in beacon_ies", + p, l); + break; + } + if (wpa_s->ap_wpa_ie == NULL && + p[0] == GENERIC_INFO_ELEM && p[1] >= 6 && + memcmp(&p[2], "\x00\x50\xF2\x01\x01\x00", 6) == 0) { + wpa_s->ap_wpa_ie = malloc(len); + if (wpa_s->ap_wpa_ie) { + memcpy(wpa_s->ap_wpa_ie, p, len); + wpa_s->ap_wpa_ie_len = len; + } + } + + if (wpa_s->ap_rsn_ie == NULL && + p[0] == RSN_INFO_ELEM && p[1] >= 2) { + wpa_s->ap_rsn_ie = malloc(len); + if (wpa_s->ap_rsn_ie) { + memcpy(wpa_s->ap_rsn_ie, p, len); + wpa_s->ap_rsn_ie_len = len; + } + + } + + l -= len; + p += len; + } + +} + + +void wpa_supplicant_event(struct wpa_supplicant *wpa_s, wpa_event_type event, + union wpa_event_data *data) +{ + int pairwise; + time_t now; + u8 bssid[ETH_ALEN]; + + switch (event) { + case EVENT_ASSOC: + wpa_s->wpa_state = WPA_ASSOCIATED; + wpa_printf(MSG_DEBUG, "Association event - clear replay " + "counter"); + memset(wpa_s->rx_replay_counter, 0, WPA_REPLAY_COUNTER_LEN); + wpa_s->rx_replay_counter_set = 0; + wpa_s->renew_snonce = 1; + if (wpa_drv_get_bssid(wpa_s, bssid) >= 0 && + memcmp(bssid, wpa_s->bssid, ETH_ALEN) != 0) { + wpa_msg(wpa_s, MSG_DEBUG, "Associated to a new BSS: " + "BSSID=" MACSTR, MAC2STR(bssid)); + memcpy(wpa_s->bssid, bssid, ETH_ALEN); + if (wpa_supplicant_dynamic_keys(wpa_s)) { + wpa_clear_keys(wpa_s, bssid); + } + wpa_supplicant_select_config(wpa_s); + } + wpa_msg(wpa_s, MSG_INFO, "Associated with " MACSTR, + MAC2STR(bssid)); + /* Set portEnabled first to FALSE in order to get EAP state + * machine out of the SUCCESS state and eapSuccess cleared. + * Without this, EAPOL PAE state machine may transit to + * AUTHENTICATING state based on obsolete eapSuccess and then + * trigger BE_AUTH to SUCCESS and PAE to AUTHENTICATED without + * ever giving chance to EAP state machine to reset the state. + */ + eapol_sm_notify_portEnabled(wpa_s->eapol, FALSE); + eapol_sm_notify_portValid(wpa_s->eapol, FALSE); + if (wpa_s->key_mgmt == WPA_KEY_MGMT_PSK) + eapol_sm_notify_eap_success(wpa_s->eapol, FALSE); + /* 802.1X::portControl = Auto */ + eapol_sm_notify_portEnabled(wpa_s->eapol, TRUE); + wpa_s->eapol_received = 0; + if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE || + wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) { + wpa_supplicant_cancel_auth_timeout(wpa_s); + } else { + /* Timeout for receiving the first EAPOL packet */ + wpa_supplicant_req_auth_timeout(wpa_s, 10, 0); + } + break; + case EVENT_DISASSOC: + if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) { + /* At least Host AP driver and a Prism3 card seemed to + * be generating streams of disconnected events when + * configuring IBSS for WPA-None. Ignore them for now. + */ + wpa_printf(MSG_DEBUG, "Disconnect event - ignore in " + "IBSS/WPA-None mode"); + break; + } + if (wpa_s->wpa_state >= WPA_ASSOCIATED) + wpa_supplicant_req_scan(wpa_s, 0, 100000); + wpa_blacklist_add(wpa_s, wpa_s->bssid); + wpa_supplicant_mark_disassoc(wpa_s); + wpa_msg(wpa_s, MSG_INFO, "Disconnect event - remove keys"); + if (wpa_supplicant_dynamic_keys(wpa_s)) { + wpa_s->keys_cleared = 0; + wpa_clear_keys(wpa_s, wpa_s->bssid); + } + break; + case EVENT_MICHAEL_MIC_FAILURE: + wpa_msg(wpa_s, MSG_WARNING, "Michael MIC failure detected"); + pairwise = (data && data->michael_mic_failure.unicast); + wpa_supplicant_key_request(wpa_s, 1, pairwise); + time(&now); + if (wpa_s->last_michael_mic_error && + now - wpa_s->last_michael_mic_error <= 60) { + /* initialize countermeasures */ + wpa_s->countermeasures = 1; + wpa_msg(wpa_s, MSG_WARNING, "TKIP countermeasures " + "started"); + + /* Need to wait for completion of request frame. We do + * not get any callback for the message completion, so + * just wait a short while and hope for the best. */ + usleep(10000); + + wpa_drv_set_countermeasures(wpa_s, 1); + wpa_supplicant_deauthenticate( + wpa_s, REASON_MICHAEL_MIC_FAILURE); + eloop_cancel_timeout( + wpa_supplicant_stop_countermeasures, wpa_s, + NULL); + eloop_register_timeout( + 60, 0, wpa_supplicant_stop_countermeasures, + wpa_s, NULL); + /* TODO: mark the AP rejected for 60 second. STA is + * allowed to associate with another AP.. */ + } + wpa_s->last_michael_mic_error = now; + break; + case EVENT_SCAN_RESULTS: + wpa_supplicant_scan_results(wpa_s); + break; + case EVENT_ASSOCINFO: + wpa_supplicant_associnfo(wpa_s, data); + break; + case EVENT_INTERFACE_STATUS: + if (strcmp(wpa_s->ifname, data->interface_status.ifname) != 0) + break; + switch (data->interface_status.ievent) { + case EVENT_INTERFACE_ADDED: + if (!wpa_s->interface_removed) + break; + wpa_s->interface_removed = 0; + wpa_printf(MSG_DEBUG, "Configured interface was " + "added."); + if (wpa_supplicant_driver_init(wpa_s, 1) < 0) { + wpa_printf(MSG_INFO, "Failed to initialize " + "the driver after interface was " + "added."); + } + break; + case EVENT_INTERFACE_REMOVED: + wpa_printf(MSG_DEBUG, "Configured interface was " + "removed."); + wpa_s->interface_removed = 1; + wpa_supplicant_mark_disassoc(wpa_s); + l2_packet_deinit(wpa_s->l2); + break; + } + break; + case EVENT_PMKID_CANDIDATE: + wpa_supplicant_add_pmkid_candidate(wpa_s, data); + break; + default: + wpa_printf(MSG_INFO, "Unknown event %d", event); + break; + } +} + + +static void wpa_supplicant_terminate(int sig, void *eloop_ctx, + void *signal_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + for (wpa_s = wpa_s->head; wpa_s; wpa_s = wpa_s->next) { + wpa_msg(wpa_s, MSG_INFO, "Signal %d received - terminating", + sig); + } + eloop_terminate(); +} + + +int wpa_supplicant_reload_configuration(struct wpa_supplicant *wpa_s) +{ + struct wpa_config *conf; + int reconf_ctrl; + if (wpa_s->confname == NULL) + return -1; + conf = wpa_config_read(wpa_s->confname); + if (conf == NULL) { + wpa_msg(wpa_s, MSG_ERROR, "Failed to parse the configuration " + "file '%s' - exiting", wpa_s->confname); + return -1; + } + + reconf_ctrl = !!conf->ctrl_interface != !!wpa_s->conf->ctrl_interface + || (conf->ctrl_interface && wpa_s->conf->ctrl_interface && + strcmp(conf->ctrl_interface, wpa_s->conf->ctrl_interface) + != 0); + + if (reconf_ctrl) + wpa_supplicant_ctrl_iface_deinit(wpa_s); + + wpa_s->current_ssid = NULL; + eapol_sm_notify_config(wpa_s->eapol, NULL, NULL); + rsn_preauth_deinit(wpa_s); + wpa_config_free(wpa_s->conf); + wpa_s->conf = conf; + if (reconf_ctrl) + wpa_supplicant_ctrl_iface_init(wpa_s); + wpa_s->reassociate = 1; + wpa_supplicant_req_scan(wpa_s, 0, 0); + wpa_msg(wpa_s, MSG_DEBUG, "Reconfiguration completed"); + return 0; +} + + +#ifndef CONFIG_NATIVE_WINDOWS +static void wpa_supplicant_reconfig(int sig, void *eloop_ctx, + void *signal_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + wpa_printf(MSG_DEBUG, "Signal %d received - reconfiguring", sig); + for (wpa_s = wpa_s->head; wpa_s; wpa_s = wpa_s->next) { + if (wpa_supplicant_reload_configuration(wpa_s) < 0) { + eloop_terminate(); + } + } +} +#endif /* CONFIG_NATIVE_WINDOWS */ + + +static void wpa_supplicant_gen_assoc_event(struct wpa_supplicant *wpa_s) +{ + struct wpa_ssid *ssid; + union wpa_event_data data; + + ssid = wpa_supplicant_get_ssid(wpa_s); + if (ssid == NULL) + return; + + wpa_printf(MSG_DEBUG, "Already associated with a configured network - " + "generating associated event"); + memset(&data, 0, sizeof(data)); + wpa_supplicant_event(wpa_s, EVENT_ASSOC, &data); +} + + +void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + struct wpa_ssid *ssid; + + if (wpa_s->conf->ap_scan == 0) { + wpa_supplicant_gen_assoc_event(wpa_s); + return; + } + + if (wpa_s->conf->ap_scan == 2) { + ssid = wpa_s->conf->ssid; + if (ssid == NULL) + return; + wpa_supplicant_associate(wpa_s, NULL, ssid); + return; + } + + if (wpa_s->wpa_state == WPA_DISCONNECTED) + wpa_s->wpa_state = WPA_SCANNING; + + ssid = wpa_s->conf->ssid; + if (wpa_s->prev_scan_ssid != BROADCAST_SSID_SCAN) { + while (ssid) { + if (ssid == wpa_s->prev_scan_ssid) { + ssid = ssid->next; + break; + } + ssid = ssid->next; + } + } + while (ssid) { + if (ssid->scan_ssid) + break; + ssid = ssid->next; + } + + wpa_printf(MSG_DEBUG, "Starting AP scan (%s SSID)", + ssid ? "specific": "broadcast"); + if (ssid) { + wpa_hexdump_ascii(MSG_DEBUG, "Scan SSID", + ssid->ssid, ssid->ssid_len); + wpa_s->prev_scan_ssid = ssid; + } else + wpa_s->prev_scan_ssid = BROADCAST_SSID_SCAN; + + if (wpa_drv_scan(wpa_s, ssid ? ssid->ssid : NULL, + ssid ? ssid->ssid_len : 0)) { + wpa_printf(MSG_WARNING, "Failed to initiate AP scan."); + wpa_supplicant_req_scan(wpa_s, 10, 0); + } +} + + +static wpa_cipher cipher_suite2driver(int cipher) +{ + switch (cipher) { + case WPA_CIPHER_NONE: + return CIPHER_NONE; + case WPA_CIPHER_WEP40: + return CIPHER_WEP40; + case WPA_CIPHER_WEP104: + return CIPHER_WEP104; + case WPA_CIPHER_CCMP: + return CIPHER_CCMP; + case WPA_CIPHER_TKIP: + default: + return CIPHER_TKIP; + } +} + + +static wpa_key_mgmt key_mgmt2driver(int key_mgmt) +{ + switch (key_mgmt) { + case WPA_KEY_MGMT_NONE: + return KEY_MGMT_NONE; + case WPA_KEY_MGMT_IEEE8021X_NO_WPA: + return KEY_MGMT_802_1X_NO_WPA; + case WPA_KEY_MGMT_IEEE8021X: + return KEY_MGMT_802_1X; + case WPA_KEY_MGMT_WPA_NONE: + return KEY_MGMT_WPA_NONE; + case WPA_KEY_MGMT_PSK: + default: + return KEY_MGMT_PSK; + } +} + + +static int wpa_supplicant_suites_from_ai(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, + struct wpa_ie_data *ie) { + if (wpa_s->assoc_wpa_ie == NULL) + return -1; + + if (wpa_parse_wpa_ie(wpa_s, wpa_s->assoc_wpa_ie, + wpa_s->assoc_wpa_ie_len, ie)) { + wpa_msg(wpa_s, MSG_INFO, "WPA: Failed to parse WPA IE from " + "association info"); + return -1; + } + + wpa_printf(MSG_DEBUG, "WPA: Using WPA IE from AssocReq to set cipher " + "suites"); + if (!(ie->group_cipher & ssid->group_cipher)) { + wpa_msg(wpa_s, MSG_INFO, "WPA: Driver used disabled group " + "cipher 0x%x (mask 0x%x) - reject", + ie->group_cipher, ssid->group_cipher); + return -1; + } + if (!(ie->pairwise_cipher & ssid->pairwise_cipher)) { + wpa_msg(wpa_s, MSG_INFO, "WPA: Driver used disabled pairwise " + "cipher 0x%x (mask 0x%x) - reject", + ie->pairwise_cipher, ssid->pairwise_cipher); + return -1; + } + if (!(ie->key_mgmt & ssid->key_mgmt)) { + wpa_msg(wpa_s, MSG_INFO, "WPA: Driver used disabled key " + "management 0x%x (mask 0x%x) - reject", + ie->key_mgmt, ssid->key_mgmt); + return -1; + } + + return 0; +} + + +static int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, + struct wpa_scan_result *bss, + struct wpa_ssid *ssid, + u8 *wpa_ie, int *wpa_ie_len) +{ + struct wpa_ie_data ie; + int sel, proto; + u8 *ap_ie; + size_t ap_ie_len; + + if (bss && bss->rsn_ie_len && (ssid->proto & WPA_PROTO_RSN)) { + wpa_msg(wpa_s, MSG_DEBUG, "RSN: using IEEE 802.11i/D9.0"); + proto = WPA_PROTO_RSN; + ap_ie = bss->rsn_ie; + ap_ie_len = bss->rsn_ie_len; + } else if (bss) { + wpa_msg(wpa_s, MSG_DEBUG, "WPA: using IEEE 802.11i/D3.0"); + proto = WPA_PROTO_WPA; + ap_ie = bss->wpa_ie; + ap_ie_len = bss->wpa_ie_len; + } else { + if (ssid->proto & WPA_PROTO_RSN) + proto = WPA_PROTO_RSN; + else + proto = WPA_PROTO_WPA; + ap_ie = NULL; + ap_ie_len = 0; + if (wpa_supplicant_suites_from_ai(wpa_s, ssid, &ie) < 0) { + memset(&ie, 0, sizeof(ie)); + ie.group_cipher = ssid->group_cipher; + ie.pairwise_cipher = ssid->pairwise_cipher; + ie.key_mgmt = ssid->key_mgmt; + wpa_printf(MSG_DEBUG, "WPA: Set cipher suites based " + "on configuration"); + } + } + + if (ap_ie && wpa_parse_wpa_ie(wpa_s, ap_ie, ap_ie_len, &ie)) { + wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to parse WPA IE for " + "the selected BSS."); + return -1; + } + wpa_printf(MSG_DEBUG, "WPA: Selected cipher suites: group %d " + "pairwise %d key_mgmt %d", + ie.group_cipher, ie.pairwise_cipher, ie.key_mgmt); + + wpa_s->proto = proto; + + free(wpa_s->ap_wpa_ie); + wpa_s->ap_wpa_ie = NULL; + wpa_s->ap_wpa_ie_len = 0; + if (bss && bss->wpa_ie_len) { + wpa_s->ap_wpa_ie = malloc(bss->wpa_ie_len); + if (wpa_s->ap_wpa_ie == NULL) { + wpa_printf(MSG_INFO, "WPA: malloc failed"); + return -1; + } + memcpy(wpa_s->ap_wpa_ie, bss->wpa_ie, bss->wpa_ie_len); + wpa_s->ap_wpa_ie_len = bss->wpa_ie_len; + } + + free(wpa_s->ap_rsn_ie); + wpa_s->ap_rsn_ie = NULL; + wpa_s->ap_rsn_ie_len = 0; + if (bss && bss->rsn_ie_len) { + wpa_s->ap_rsn_ie = malloc(bss->rsn_ie_len); + if (wpa_s->ap_rsn_ie == NULL) { + wpa_printf(MSG_INFO, "WPA: malloc failed"); + return -1; + } + memcpy(wpa_s->ap_rsn_ie, bss->rsn_ie, bss->rsn_ie_len); + wpa_s->ap_rsn_ie_len = bss->rsn_ie_len; + } + + sel = ie.group_cipher & ssid->group_cipher; + if (sel & WPA_CIPHER_CCMP) { + wpa_s->group_cipher = WPA_CIPHER_CCMP; + wpa_msg(wpa_s, MSG_DEBUG, "WPA: using GTK CCMP"); + } else if (sel & WPA_CIPHER_TKIP) { + wpa_s->group_cipher = WPA_CIPHER_TKIP; + wpa_msg(wpa_s, MSG_DEBUG, "WPA: using GTK TKIP"); + } else if (sel & WPA_CIPHER_WEP104) { + wpa_s->group_cipher = WPA_CIPHER_WEP104; + wpa_msg(wpa_s, MSG_DEBUG, "WPA: using GTK WEP104"); + } else if (sel & WPA_CIPHER_WEP40) { + wpa_s->group_cipher = WPA_CIPHER_WEP40; + wpa_msg(wpa_s, MSG_DEBUG, "WPA: using GTK WEP40"); + } else { + wpa_printf(MSG_WARNING, "WPA: Failed to select group cipher."); + return -1; + } + + sel = ie.pairwise_cipher & ssid->pairwise_cipher; + if (sel & WPA_CIPHER_CCMP) { + wpa_s->pairwise_cipher = WPA_CIPHER_CCMP; + wpa_msg(wpa_s, MSG_DEBUG, "WPA: using PTK CCMP"); + } else if (sel & WPA_CIPHER_TKIP) { + wpa_s->pairwise_cipher = WPA_CIPHER_TKIP; + wpa_msg(wpa_s, MSG_DEBUG, "WPA: using PTK TKIP"); + } else if (sel & WPA_CIPHER_NONE) { + wpa_s->pairwise_cipher = WPA_CIPHER_NONE; + wpa_msg(wpa_s, MSG_DEBUG, "WPA: using PTK NONE"); + } else { + wpa_printf(MSG_WARNING, "WPA: Failed to select pairwise " + "cipher."); + return -1; + } + + sel = ie.key_mgmt & ssid->key_mgmt; + if (sel & WPA_KEY_MGMT_IEEE8021X) { + wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X; + wpa_msg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT 802.1X"); + } else if (sel & WPA_KEY_MGMT_PSK) { + wpa_s->key_mgmt = WPA_KEY_MGMT_PSK; + wpa_msg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT WPA-PSK"); + } else if (sel & WPA_KEY_MGMT_WPA_NONE) { + wpa_s->key_mgmt = WPA_KEY_MGMT_WPA_NONE; + wpa_msg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT WPA-NONE"); + } else { + wpa_printf(MSG_WARNING, "WPA: Failed to select authenticated " + "key management type."); + return -1; + } + + *wpa_ie_len = wpa_gen_wpa_ie(wpa_s, wpa_ie); + if (*wpa_ie_len < 0) { + wpa_printf(MSG_WARNING, "WPA: Failed to generate WPA IE."); + return -1; + } + wpa_hexdump(MSG_DEBUG, "WPA: Own WPA IE", wpa_ie, *wpa_ie_len); + + if (ssid->key_mgmt & WPA_KEY_MGMT_PSK) { + wpa_s->pmk_len = PMK_LEN; + memcpy(wpa_s->pmk, ssid->psk, PMK_LEN); + } else if (wpa_s->cur_pmksa) { + wpa_s->pmk_len = wpa_s->cur_pmksa->pmk_len; + memcpy(wpa_s->pmk, wpa_s->cur_pmksa->pmk, wpa_s->pmk_len); + } else { + wpa_s->pmk_len = PMK_LEN; + memset(wpa_s->pmk, 0, PMK_LEN); +#ifdef CONFIG_XSUPPLICANT_IFACE + wpa_s->ext_pmk_received = 0; +#endif /* CONFIG_XSUPPLICANT_IFACE */ + } + + return 0; +} + + +static void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, + struct wpa_scan_result *bss, + struct wpa_ssid *ssid) +{ + u8 wpa_ie[80]; + int wpa_ie_len; + int use_crypt; + int algs = AUTH_ALG_OPEN_SYSTEM; + int cipher_pairwise, cipher_group; + struct wpa_driver_associate_params params; + int wep_keys_set = 0; + struct wpa_driver_capa capa; + + wpa_s->reassociate = 0; + if (bss) { + wpa_msg(wpa_s, MSG_INFO, "Trying to associate with " MACSTR + " (SSID='%s' freq=%d MHz)", MAC2STR(bss->bssid), + wpa_ssid_txt(ssid->ssid, ssid->ssid_len), bss->freq); + memset(wpa_s->bssid, 0, ETH_ALEN); + } else { + wpa_msg(wpa_s, MSG_INFO, "Trying to associate with SSID '%s'", + wpa_ssid_txt(ssid->ssid, ssid->ssid_len)); + } + wpa_supplicant_cancel_scan(wpa_s); + + /* Starting new association, so clear the possibly used WPA IE from the + * previous association. */ + free(wpa_s->assoc_wpa_ie); + wpa_s->assoc_wpa_ie = NULL; + wpa_s->assoc_wpa_ie_len = 0; + + if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) { + if (ssid->leap) { + if (ssid->non_leap == 0) + algs = AUTH_ALG_LEAP; + else + algs |= AUTH_ALG_LEAP; + } + } + wpa_printf(MSG_DEBUG, "Automatic auth_alg selection: 0x%x", algs); + if (ssid->auth_alg) { + algs = 0; + if (ssid->auth_alg & WPA_AUTH_ALG_OPEN) + algs |= AUTH_ALG_OPEN_SYSTEM; + if (ssid->auth_alg & WPA_AUTH_ALG_SHARED) + algs |= AUTH_ALG_SHARED_KEY; + if (ssid->auth_alg & WPA_AUTH_ALG_LEAP) + algs |= AUTH_ALG_LEAP; + wpa_printf(MSG_DEBUG, "Overriding auth_alg selection: 0x%x", + algs); + } + wpa_drv_set_auth_alg(wpa_s, algs); + + if (bss && (bss->wpa_ie_len || bss->rsn_ie_len) && + (ssid->key_mgmt & (WPA_KEY_MGMT_IEEE8021X | WPA_KEY_MGMT_PSK))) { + wpa_s->cur_pmksa = pmksa_cache_get(wpa_s, bss->bssid, NULL); + if (wpa_s->cur_pmksa) { + wpa_hexdump(MSG_DEBUG, "RSN: PMKID", + wpa_s->cur_pmksa->pmkid, PMKID_LEN); + eapol_sm_notify_pmkid_attempt(wpa_s->eapol, 1); + } + if (wpa_supplicant_set_suites(wpa_s, bss, ssid, + wpa_ie, &wpa_ie_len)) { + wpa_printf(MSG_WARNING, "WPA: Failed to set WPA key " + "management and encryption suites"); + return; + } + } else if (ssid->key_mgmt & + (WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_IEEE8021X | + WPA_KEY_MGMT_WPA_NONE)) { + if (wpa_supplicant_set_suites(wpa_s, NULL, ssid, + wpa_ie, &wpa_ie_len)) { + wpa_printf(MSG_WARNING, "WPA: Failed to set WPA key " + "management and encryption suites (no scan " + "results)"); + return; + } + } else { + wpa_supplicant_set_non_wpa_policy(wpa_s, ssid); + wpa_ie_len = 0; + } + + wpa_clear_keys(wpa_s, bss ? bss->bssid : NULL); + use_crypt = 1; + cipher_pairwise = cipher_suite2driver(wpa_s->pairwise_cipher); + cipher_group = cipher_suite2driver(wpa_s->group_cipher); + if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE || + wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) { + int i; + if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE) + use_crypt = 0; + for (i = 0; i < NUM_WEP_KEYS; i++) { + if (ssid->wep_key_len[i]) { + use_crypt = 1; + wep_keys_set = 1; + wpa_set_wep_key(wpa_s, + i == ssid->wep_tx_keyidx, + i, ssid->wep_key[i], + ssid->wep_key_len[i]); + } + } + } + + if (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) { + if ((ssid->eapol_flags & + (EAPOL_FLAG_REQUIRE_KEY_UNICAST | + EAPOL_FLAG_REQUIRE_KEY_BROADCAST)) == 0 && + !wep_keys_set) { + use_crypt = 0; + } else { + /* Assume that dynamic WEP-104 keys will be used and + * set cipher suites in order for drivers to expect + * encryption. */ + cipher_pairwise = cipher_group = CIPHER_WEP104; + } + } + + if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) { + /* Set the key before (and later after) association */ + wpa_supplicant_set_wpa_none_key(wpa_s, ssid); + } + + wpa_drv_set_drop_unencrypted(wpa_s, use_crypt); + wpa_s->wpa_state = WPA_ASSOCIATING; + memset(¶ms, 0, sizeof(params)); + if (bss) { + params.bssid = bss->bssid; + params.ssid = bss->ssid; + params.ssid_len = bss->ssid_len; + params.freq = bss->freq; + } else { + params.ssid = ssid->ssid; + params.ssid_len = ssid->ssid_len; + } + params.wpa_ie = wpa_ie; + params.wpa_ie_len = wpa_ie_len; + params.pairwise_suite = cipher_pairwise; + params.group_suite = cipher_group; + params.key_mgmt_suite = key_mgmt2driver(wpa_s->key_mgmt); + params.auth_alg = algs; + params.mode = ssid->mode; + if (wpa_drv_associate(wpa_s, ¶ms) < 0) { + wpa_msg(wpa_s, MSG_INFO, "Association request to the driver " + "failed"); + /* try to continue anyway; new association will be tried again + * after timeout */ + } + + if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) { + /* Set the key after the association just in case association + * cleared the previously configured key. */ + wpa_supplicant_set_wpa_none_key(wpa_s, ssid); + /* No need to timeout authentication since there is no key + * management. */ + wpa_supplicant_cancel_auth_timeout(wpa_s); + } else { + /* Timeout for IEEE 802.11 authentication and association */ + wpa_supplicant_req_auth_timeout(wpa_s, 5, 0); + } + + if (wep_keys_set && wpa_drv_get_capa(wpa_s, &capa) == 0 && + capa.flags & WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC) { + /* Set static WEP keys again */ + int i; + for (i = 0; i < NUM_WEP_KEYS; i++) { + if (ssid->wep_key_len[i]) { + wpa_set_wep_key(wpa_s, + i == ssid->wep_tx_keyidx, + i, ssid->wep_key[i], + ssid->wep_key_len[i]); + } + } + } + + wpa_s->current_ssid = ssid; + wpa_supplicant_initiate_eapol(wpa_s); +} + + +void wpa_supplicant_disassociate(struct wpa_supplicant *wpa_s, + int reason_code) +{ + u8 *addr = NULL; + wpa_s->wpa_state = WPA_DISCONNECTED; + if (memcmp(wpa_s->bssid, "\x00\x00\x00\x00\x00\x00", ETH_ALEN) != 0) { + wpa_drv_disassociate(wpa_s, wpa_s->bssid, reason_code); + addr = wpa_s->bssid; + } + wpa_clear_keys(wpa_s, addr); + wpa_s->current_ssid = NULL; + eapol_sm_notify_config(wpa_s->eapol, NULL, NULL); + eapol_sm_notify_portEnabled(wpa_s->eapol, FALSE); + eapol_sm_notify_portValid(wpa_s->eapol, FALSE); +} + + +void wpa_supplicant_deauthenticate(struct wpa_supplicant *wpa_s, + int reason_code) +{ + u8 *addr = NULL; + wpa_s->wpa_state = WPA_DISCONNECTED; + if (memcmp(wpa_s->bssid, "\x00\x00\x00\x00\x00\x00", ETH_ALEN) != 0) { + wpa_drv_deauthenticate(wpa_s, wpa_s->bssid, reason_code); + addr = wpa_s->bssid; + } + wpa_clear_keys(wpa_s, addr); + wpa_s->current_ssid = NULL; + eapol_sm_notify_config(wpa_s->eapol, NULL, NULL); + eapol_sm_notify_portEnabled(wpa_s->eapol, FALSE); + eapol_sm_notify_portValid(wpa_s->eapol, FALSE); +} + + +static void wpa_supplicant_imsi_identity(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ + int aka = 0; + u8 *pos = ssid->eap_methods; + + while (pos && *pos != EAP_TYPE_NONE) { + if (*pos == EAP_TYPE_AKA) { + aka = 1; + break; + } + pos++; + } + + if (ssid->identity == NULL && wpa_s->imsi) { + ssid->identity = malloc(1 + wpa_s->imsi_len); + if (ssid->identity) { + ssid->identity[0] = aka ? '0' : '1'; + memcpy(ssid->identity + 1, wpa_s->imsi, + wpa_s->imsi_len); + ssid->identity_len = 1 + wpa_s->imsi_len; + wpa_hexdump_ascii(MSG_DEBUG, "permanent identity from " + "IMSI", ssid->identity, + ssid->identity_len); + } + } +} + + +static void wpa_supplicant_scard_init(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ + char buf[100]; + size_t len; + + if (ssid->pcsc == NULL) + return; + if (wpa_s->scard != NULL) { + wpa_supplicant_imsi_identity(wpa_s, ssid); + return; + } + wpa_printf(MSG_DEBUG, "Selected network is configured to use SIM - " + "initialize PCSC"); + wpa_s->scard = scard_init(SCARD_TRY_BOTH, ssid->pin); + if (wpa_s->scard == NULL) { + wpa_printf(MSG_WARNING, "Failed to initialize SIM " + "(pcsc-lite)"); + /* TODO: what to do here? */ + return; + } + eapol_sm_register_scard_ctx(wpa_s->eapol, wpa_s->scard); + + len = sizeof(buf); + if (scard_get_imsi(wpa_s->scard, buf, &len)) { + wpa_printf(MSG_WARNING, "Failed to get IMSI from SIM"); + /* TODO: what to do here? */ + return; + } + + wpa_hexdump_ascii(MSG_DEBUG, "IMSI", (u8 *) buf, len); + free(wpa_s->imsi); + wpa_s->imsi = malloc(len); + if (wpa_s->imsi) { + memcpy(wpa_s->imsi, buf, len); + wpa_s->imsi_len = len; + wpa_supplicant_imsi_identity(wpa_s, ssid); + } +} + + +static struct wpa_scan_result * +wpa_supplicant_select_bss(struct wpa_supplicant *wpa_s, struct wpa_ssid *group, + struct wpa_scan_result *results, int num, + struct wpa_ssid **selected_ssid) +{ + struct wpa_ssid *ssid; + struct wpa_scan_result *bss, *selected = NULL; + int i; + + wpa_printf(MSG_DEBUG, "Selecting BSS from priority group %d", + group->priority); + + bss = NULL; + ssid = NULL; + /* First, try to find WPA-enabled AP */ + for (i = 0; i < num && !selected; i++) { + bss = &results[i]; + wpa_printf(MSG_DEBUG, "%d: " MACSTR " ssid='%s' " + "wpa_ie_len=%lu rsn_ie_len=%lu", + i, MAC2STR(bss->bssid), + wpa_ssid_txt(bss->ssid, bss->ssid_len), + (unsigned long) bss->wpa_ie_len, + (unsigned long) bss->rsn_ie_len); + if (wpa_blacklisted(wpa_s, bss->bssid)) { + wpa_printf(MSG_DEBUG, " skip - blacklisted"); + continue; + } + + if (bss->wpa_ie_len == 0 && bss->rsn_ie_len == 0) { + wpa_printf(MSG_DEBUG, " skip - no WPA/RSN IE"); + continue; + } + + for (ssid = group; ssid; ssid = ssid->pnext) { + struct wpa_ie_data ie; + if (bss->ssid_len != ssid->ssid_len || + memcmp(bss->ssid, ssid->ssid, + bss->ssid_len) != 0) { + wpa_printf(MSG_DEBUG, " skip - " + "SSID mismatch"); + continue; + } + if (ssid->bssid_set && + memcmp(bss->bssid, ssid->bssid, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, " skip - " + "BSSID mismatch"); + continue; + } + if (!(((ssid->proto & WPA_PROTO_RSN) && + wpa_parse_wpa_ie(wpa_s, bss->rsn_ie, + bss->rsn_ie_len, &ie) == 0) || + ((ssid->proto & WPA_PROTO_WPA) && + wpa_parse_wpa_ie(wpa_s, bss->wpa_ie, + bss->wpa_ie_len, &ie) == 0))) { + wpa_printf(MSG_DEBUG, " skip - " + "could not parse WPA/RSN IE"); + continue; + } + if (!(ie.proto & ssid->proto)) { + wpa_printf(MSG_DEBUG, " skip - " + "proto mismatch"); + continue; + } + if (!(ie.pairwise_cipher & ssid->pairwise_cipher)) { + wpa_printf(MSG_DEBUG, " skip - " + "PTK cipher mismatch"); + continue; + } + if (!(ie.group_cipher & ssid->group_cipher)) { + wpa_printf(MSG_DEBUG, " skip - " + "GTK cipher mismatch"); + continue; + } + if (!(ie.key_mgmt & ssid->key_mgmt)) { + wpa_printf(MSG_DEBUG, " skip - " + "key mgmt mismatch"); + continue; + } + + selected = bss; + *selected_ssid = ssid; + wpa_printf(MSG_DEBUG, " selected"); + break; + } + } + + /* If no WPA-enabled AP found, try to find non-WPA AP, if configuration + * allows this. */ + for (i = 0; i < num && !selected; i++) { + bss = &results[i]; + if (wpa_blacklisted(wpa_s, bss->bssid)) { + continue; + } + for (ssid = group; ssid; ssid = ssid->pnext) { + if (bss->ssid_len == ssid->ssid_len && + memcmp(bss->ssid, ssid->ssid, bss->ssid_len) == 0 + && + (!ssid->bssid_set || + memcmp(bss->bssid, ssid->bssid, ETH_ALEN) == 0) && + ((ssid->key_mgmt & WPA_KEY_MGMT_NONE) || + (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA))) + { + selected = bss; + *selected_ssid = ssid; + wpa_printf(MSG_DEBUG, " selected non-WPA AP " + MACSTR " ssid='%s'", + MAC2STR(bss->bssid), + wpa_ssid_txt(bss->ssid, + bss->ssid_len)); + break; + } + } + } + + return selected; +} + + +static int wpa_supplicant_get_scan_results(struct wpa_supplicant *wpa_s) +{ +#define SCAN_AP_LIMIT 128 + struct wpa_scan_result *results, *tmp; + int num; + + results = malloc(SCAN_AP_LIMIT * sizeof(struct wpa_scan_result)); + if (results == NULL) { + wpa_printf(MSG_WARNING, "Failed to allocate memory for scan " + "results"); + return -1; + } + + num = wpa_drv_get_scan_results(wpa_s, results, SCAN_AP_LIMIT); + wpa_printf(MSG_DEBUG, "Scan results: %d", num); + if (num < 0) { + wpa_printf(MSG_DEBUG, "Failed to get scan results"); + free(results); + return -1; + } + if (num > SCAN_AP_LIMIT) { + wpa_printf(MSG_INFO, "Not enough room for all APs (%d < %d)", + num, SCAN_AP_LIMIT); + num = SCAN_AP_LIMIT; + } + + /* Free unneeded memory for unused scan result entries */ + tmp = realloc(results, num * sizeof(struct wpa_scan_result)); + if (tmp || num == 0) { + results = tmp; + } + + free(wpa_s->scan_results); + wpa_s->scan_results = results; + wpa_s->num_scan_results = num; + + return 0; +} + + +static void wpa_supplicant_scan_results(struct wpa_supplicant *wpa_s) +{ + int num, prio; + struct wpa_scan_result *selected = NULL; + struct wpa_ssid *ssid; + struct wpa_scan_result *results; + + if (wpa_supplicant_get_scan_results(wpa_s) < 0) { + wpa_printf(MSG_DEBUG, "Failed to get scan results - try " + "scanning again"); + wpa_supplicant_req_scan(wpa_s, 1, 0); + return; + } + results = wpa_s->scan_results; + num = wpa_s->num_scan_results; + + while (selected == NULL) { + for (prio = 0; prio < wpa_s->conf->num_prio; prio++) { + selected = wpa_supplicant_select_bss( + wpa_s, wpa_s->conf->pssid[prio], results, num, + &ssid); + if (selected) + break; + } + + if (selected == NULL && wpa_s->blacklist) { + wpa_printf(MSG_DEBUG, "No APs found - clear blacklist " + "and try again"); + wpa_blacklist_clear(wpa_s); + } else if (selected == NULL) { + break; + } + } + + if (selected) { + if (wpa_s->reassociate || + memcmp(selected->bssid, wpa_s->bssid, ETH_ALEN) != 0) { + wpa_supplicant_scard_init(wpa_s, ssid); + wpa_supplicant_associate(wpa_s, selected, ssid); + } else { + wpa_printf(MSG_DEBUG, "Already associated with the " + "selected AP."); + } + rsn_preauth_scan_results(wpa_s, results, num); + } else { + wpa_printf(MSG_DEBUG, "No suitable AP found."); + wpa_supplicant_req_scan(wpa_s, 5, 0); + } +} + + +static int wpa_get_beacon_ie(struct wpa_supplicant *wpa_s) +{ + int i, ret = 0; + struct wpa_scan_result *results, *curr = NULL; + + results = wpa_s->scan_results; + if (results == NULL) { + return -1; + } + + for (i = 0; i < wpa_s->num_scan_results; i++) { + if (memcmp(results[i].bssid, wpa_s->bssid, ETH_ALEN) == 0) { + curr = &results[i]; + break; + } + } + + if (curr) { + free(wpa_s->ap_wpa_ie); + wpa_s->ap_wpa_ie_len = curr->wpa_ie_len; + if (curr->wpa_ie_len) { + wpa_s->ap_wpa_ie = malloc(wpa_s->ap_wpa_ie_len); + if (wpa_s->ap_wpa_ie) { + memcpy(wpa_s->ap_wpa_ie, curr->wpa_ie, + curr->wpa_ie_len); + } else { + ret = -1; + } + } else { + wpa_s->ap_wpa_ie = NULL; + } + + free(wpa_s->ap_rsn_ie); + wpa_s->ap_rsn_ie_len = curr->rsn_ie_len; + if (curr->rsn_ie_len) { + wpa_s->ap_rsn_ie = malloc(wpa_s->ap_rsn_ie_len); + if (wpa_s->ap_rsn_ie) { + memcpy(wpa_s->ap_rsn_ie, curr->rsn_ie, + curr->rsn_ie_len); + } else { + ret = -1; + } + } else { + wpa_s->ap_rsn_ie = NULL; + } + } else { + ret = -1; + } + + return ret; +} + + +int wpa_supplicant_get_beacon_ie(struct wpa_supplicant *wpa_s) +{ + if (wpa_get_beacon_ie(wpa_s) == 0) { + return 0; + } + + /* No WPA/RSN IE found in the cached scan results. Try to get updated + * scan results from the driver. */ + if (wpa_supplicant_get_scan_results(wpa_s) < 0) { + return -1; + } + + return wpa_get_beacon_ie(wpa_s); +} + + +#ifdef CONFIG_XSUPPLICANT_IFACE +static void wpa_supplicant_dot1x_receive(int sock, void *eloop_ctx, + void *sock_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + u8 buf[128]; + int res; + + res = recv(sock, buf, sizeof(buf), 0); + wpa_printf(MSG_DEBUG, "WPA: Receive from dot1x (Xsupplicant) socket " + "==> %d", res); + if (res < 0) { + perror("recv"); + return; + } + + if (res != PMK_LEN) { + wpa_printf(MSG_WARNING, "WPA: Invalid master key length (%d) " + "from dot1x", res); + return; + } + + wpa_hexdump(MSG_DEBUG, "WPA: Master key (dot1x)", buf, PMK_LEN); + if (wpa_s->key_mgmt & WPA_KEY_MGMT_IEEE8021X) { + memcpy(wpa_s->pmk, buf, PMK_LEN); + wpa_s->ext_pmk_received = 1; + } else { + wpa_printf(MSG_INFO, "WPA: Not in IEEE 802.1X mode - dropping " + "dot1x PMK update (%d)", wpa_s->key_mgmt); + } +} + + +static int wpa_supplicant_802_1x_init(struct wpa_supplicant *wpa_s) +{ + int s; + struct sockaddr_un addr; + + s = socket(AF_LOCAL, SOCK_DGRAM, 0); + if (s < 0) { + perror("socket"); + return -1; + } + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_LOCAL; + addr.sun_path[0] = '\0'; + snprintf(addr.sun_path + 1, sizeof(addr.sun_path) - 1, + "wpa_supplicant"); + if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + perror("bind"); + close(s); + return -1; + } + + wpa_s->dot1x_s = s; + eloop_register_read_sock(s, wpa_supplicant_dot1x_receive, wpa_s, + NULL); + return 0; +} +#endif /* CONFIG_XSUPPLICANT_IFACE */ + + +static int wpa_supplicant_set_driver(struct wpa_supplicant *wpa_s, + const char *name) +{ + int i; + + if (wpa_s == NULL) + return -1; + + if (wpa_supplicant_drivers[0] == NULL) { + wpa_printf(MSG_ERROR, "No driver interfaces build into " + "wpa_supplicant."); + return -1; + } + + if (name == NULL) { + /* default to first driver in the list */ + wpa_s->driver = wpa_supplicant_drivers[0]; + return 0; + } + + for (i = 0; wpa_supplicant_drivers[i]; i++) { + if (strcmp(name, wpa_supplicant_drivers[i]->name) == 0) { + wpa_s->driver = wpa_supplicant_drivers[i]; + return 0; + } + } + + printf("Unsupported driver '%s'.\n", name); + return -1; +} + + +static void wpa_supplicant_fd_workaround(void) +{ + int s, i; + /* When started from pcmcia-cs scripts, wpa_supplicant might start with + * fd 0, 1, and 2 closed. This will cause some issues because many + * places in wpa_supplicant are still printing out to stdout. As a + * workaround, make sure that fd's 0, 1, and 2 are not used for other + * sockets. */ + for (i = 0; i < 3; i++) { + s = open("/dev/null", O_RDWR); + if (s > 2) { + close(s); + break; + } + } +} + + +static int wpa_supplicant_driver_init(struct wpa_supplicant *wpa_s, + int wait_for_interface) +{ + static int interface_count = 0; + + for (;;) { + wpa_s->l2 = l2_packet_init(wpa_s->ifname, + wpa_drv_get_mac_addr(wpa_s), + ETH_P_EAPOL, + wpa_supplicant_rx_eapol, wpa_s); + if (wpa_s->l2) + break; + else if (!wait_for_interface) + return -1; + printf("Waiting for interface..\n"); + sleep(5); + } + + if (l2_packet_get_own_addr(wpa_s->l2, wpa_s->own_addr)) { + fprintf(stderr, "Failed to get own L2 address\n"); + return -1; + } + + wpa_printf(MSG_DEBUG, "Own MAC address: " MACSTR, + MAC2STR(wpa_s->own_addr)); + + if (wpa_drv_set_wpa(wpa_s, 1) < 0) { + fprintf(stderr, "Failed to enable WPA in the driver.\n"); + return -1; + } + + wpa_clear_keys(wpa_s, NULL); + + /* Make sure that TKIP countermeasures are not left enabled (could + * happen if wpa_supplicant is killed during countermeasures. */ + wpa_drv_set_countermeasures(wpa_s, 0); + + wpa_drv_set_drop_unencrypted(wpa_s, 1); + + wpa_s->prev_scan_ssid = BROADCAST_SSID_SCAN; + wpa_supplicant_req_scan(wpa_s, interface_count, 100000); + interface_count++; + + return 0; +} + + +static void usage(void) +{ + int i; + printf("%s\n\n%s\n" + "usage:\n" + " wpa_supplicant [-BddehLqqvw] -i -c " + "[-D] \\\n" + " [-N -i -c [-D] ...]\n" + "\n" + "drivers:\n", + wpa_supplicant_version, wpa_supplicant_license); + + for (i = 0; wpa_supplicant_drivers[i]; i++) { + printf(" %s = %s\n", + wpa_supplicant_drivers[i]->name, + wpa_supplicant_drivers[i]->desc); + } + + printf("options:\n" + " -B = run daemon in the background\n" + " -d = increase debugging verbosity (-dd even more)\n" + " -K = include keys (passwords, etc.) in debug output\n" + " -t = include timestamp in debug messages\n" +#ifdef CONFIG_XSUPPLICANT_IFACE +#ifdef IEEE8021X_EAPOL + " -e = use external IEEE 802.1X Supplicant (e.g., " + "xsupplicant)\n" + " (this disables the internal Supplicant)\n" +#endif /* IEEE8021X_EAPOL */ +#endif /* CONFIG_XSUPPLICANT_IFACE */ + " -h = show this help text\n" + " -L = show license (GPL and BSD)\n" + " -q = decrease debugging verbosity (-qq even less)\n" + " -v = show version\n" + " -w = wait for interface to be added, if needed\n" + " -N = start describing new interface\n"); +} + + +static void license(void) +{ + printf("%s\n\n%s\n", + wpa_supplicant_version, wpa_supplicant_full_license); +} + + +static struct wpa_supplicant * wpa_supplicant_alloc(void) +{ + struct wpa_supplicant *wpa_s; + + wpa_s = malloc(sizeof(*wpa_s)); + if (wpa_s == NULL) + return NULL; + memset(wpa_s, 0, sizeof(*wpa_s)); +#ifdef CONFIG_XSUPPLICANT_IFACE + wpa_s->dot1x_s = -1; +#endif /* CONFIG_XSUPPLICANT_IFACE */ + + return wpa_s; +} + + +static int wpa_supplicant_init(struct wpa_supplicant *wpa_s, + const char *confname, const char *driver, + const char *ifname) +{ + wpa_printf(MSG_DEBUG, "Initializing interface '%s' conf '%s' driver " + "'%s'", ifname, confname, driver ? driver : "default"); + + if (wpa_supplicant_set_driver(wpa_s, driver) < 0) { + return -1; + } + + if (confname) { + wpa_s->confname = rel2abs_path(confname); + if (wpa_s->confname == NULL) { + wpa_printf(MSG_ERROR, "Failed to get absolute path " + "for configuration file '%s'.", confname); + return -1; + } + wpa_printf(MSG_DEBUG, "Configuration file '%s' -> '%s'", + confname, wpa_s->confname); + wpa_s->conf = wpa_config_read(wpa_s->confname); + if (wpa_s->conf == NULL) { + printf("Failed to read configuration file '%s'.\n", + wpa_s->confname); + return -1; + } + } + + if (wpa_s->conf == NULL || wpa_s->conf->ssid == NULL) { + usage(); + printf("\nNo networks (SSID) configured.\n"); + return -1; + } + + if (ifname == NULL) { + usage(); + printf("\nInterface name is required.\n"); + return -1; + } + if (strlen(ifname) >= sizeof(wpa_s->ifname)) { + printf("Too long interface name '%s'.\n", ifname); + return -1; + } + strncpy(wpa_s->ifname, ifname, sizeof(wpa_s->ifname)); + + return 0; +} + + +static int wpa_supplicant_init2(struct wpa_supplicant *wpa_s, + int disable_eapol, int wait_for_interface) +{ + const char *ifname; + + wpa_printf(MSG_DEBUG, "Initializing interface (2) '%s'", + wpa_s->ifname); + + if (!disable_eapol) { + struct eapol_ctx *ctx; + ctx = malloc(sizeof(*ctx)); + if (ctx == NULL) { + printf("Failed to allocate EAPOL context.\n"); + return -1; + } + memset(ctx, 0, sizeof(*ctx)); + ctx->ctx = wpa_s; + ctx->msg_ctx = wpa_s; + ctx->preauth = 0; + ctx->eapol_done_cb = wpa_supplicant_notify_eapol_done; + ctx->eapol_send = wpa_eapol_send; + ctx->set_wep_key = wpa_eapol_set_wep_key; + wpa_s->eapol = eapol_sm_init(ctx); + if (wpa_s->eapol == NULL) { + free(ctx); + printf("Failed to initialize EAPOL state machines.\n"); + return -1; + } + } + + /* RSNA Supplicant Key Management - INITIALIZE */ + eapol_sm_notify_portEnabled(wpa_s->eapol, FALSE); + eapol_sm_notify_portValid(wpa_s->eapol, FALSE); + + /* Initialize driver interface and register driver event handler before + * L2 receive handler so that association events are processed before + * EAPOL-Key packets if both become available for the same select() + * call. */ + wpa_s->drv_priv = wpa_drv_init(wpa_s, wpa_s->ifname); + if (wpa_s->drv_priv == NULL) { + fprintf(stderr, "Failed to initialize driver interface\n"); + return -1; + } + + ifname = wpa_drv_get_ifname(wpa_s); + if (ifname && strcmp(ifname, wpa_s->ifname) != 0) { + wpa_printf(MSG_DEBUG, "Driver interface replaced interface " + "name with '%s'", ifname); + strncpy(wpa_s->ifname, ifname, sizeof(wpa_s->ifname)); + } + + wpa_s->renew_snonce = 1; + if (wpa_supplicant_driver_init(wpa_s, wait_for_interface) < 0) { + return -1; + } + + if (wpa_supplicant_ctrl_iface_init(wpa_s)) { + printf("Failed to initialize control interface '%s'.\n" + "You may have another wpa_supplicant process already " + "running or the file was\n" + "left by an unclean termination of wpa_supplicant in " + "which case you will need\n" + "to manually remove this file before starting " + "wpa_supplicant again.\n", + wpa_s->conf->ctrl_interface); + return -1; + } + +#ifdef CONFIG_XSUPPLICANT_IFACE + if (disable_eapol) + wpa_supplicant_802_1x_init(wpa_s); +#endif /* CONFIG_XSUPPLICANT_IFACE */ + + return 0; +} + + +static void wpa_supplicant_deinit(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->drv_priv) { + if (wpa_drv_set_wpa(wpa_s, 0) < 0) { + fprintf(stderr, "Failed to disable WPA in the " + "driver.\n"); + } + + wpa_drv_set_drop_unencrypted(wpa_s, 0); + wpa_drv_set_countermeasures(wpa_s, 0); + + wpa_drv_deinit(wpa_s); + } + wpa_supplicant_cleanup(wpa_s); +} + + +int main(int argc, char *argv[]) +{ + struct wpa_supplicant *head, *wpa_s; + int c; + const char *confname, *driver, *ifname; + int daemonize = 0, wait_for_interface = 0, disable_eapol = 0, exitcode; + +#ifdef CONFIG_NATIVE_WINDOWS + WSADATA wsaData; + if (WSAStartup(MAKEWORD(2, 0), &wsaData)) { + printf("Could not find a usable WinSock.dll\n"); + return -1; + } +#endif /* CONFIG_NATIVE_WINDOWS */ + + head = wpa_s = wpa_supplicant_alloc(); + if (wpa_s == NULL) + return -1; + wpa_s->head = head; + + wpa_supplicant_fd_workaround(); + eloop_init(head); + + ifname = confname = driver = NULL; + + for (;;) { + c = getopt(argc, argv, "Bc:D:dehi:KLNqtvw"); + if (c < 0) + break; + switch (c) { + case 'B': + daemonize++; + break; + case 'c': + confname = optarg; + break; + case 'D': + driver = optarg; + break; + case 'd': + wpa_debug_level--; + break; +#ifdef CONFIG_XSUPPLICANT_IFACE +#ifdef IEEE8021X_EAPOL + case 'e': + disable_eapol++; + break; +#endif /* IEEE8021X_EAPOL */ +#endif /* CONFIG_XSUPPLICANT_IFACE */ + case 'h': + usage(); + return -1; + case 'i': + ifname = optarg; + break; + case 'K': + wpa_debug_show_keys++; + break; + case 'L': + license(); + return -1; + case 'q': + wpa_debug_level++; + break; + case 't': + wpa_debug_timestamp++; + break; + case 'v': + printf("%s\n", wpa_supplicant_version); + return -1; + case 'w': + wait_for_interface++; + break; + case 'N': + if (wpa_supplicant_init(wpa_s, confname, driver, + ifname)) + return -1; + wpa_s->next = wpa_supplicant_alloc(); + wpa_s = wpa_s->next; + if (wpa_s == NULL) + return -1; + wpa_s->head = head; + ifname = confname = driver = NULL; + break; + default: + usage(); + return -1; + } + } + + if (wpa_supplicant_init(wpa_s, confname, driver, ifname)) + return -1; + + exitcode = 0; + + if (wait_for_interface && daemonize) { + wpa_printf(MSG_DEBUG, "Daemonize.."); + if (daemon(0, 0)) { + perror("daemon"); + exitcode = -1; + goto cleanup; + } + } + + for (wpa_s = head; wpa_s; wpa_s = wpa_s->next) { + if (wpa_supplicant_init2(wpa_s, disable_eapol, + wait_for_interface)) { + exitcode = -1; + goto cleanup; + } + } + + if (!wait_for_interface && daemonize) { + wpa_printf(MSG_DEBUG, "Daemonize.."); + if (daemon(0, 0)) { + perror("daemon"); + exitcode = -1; + goto cleanup; + } + } + + eloop_register_signal(SIGINT, wpa_supplicant_terminate, NULL); + eloop_register_signal(SIGTERM, wpa_supplicant_terminate, NULL); +#ifndef CONFIG_NATIVE_WINDOWS + eloop_register_signal(SIGHUP, wpa_supplicant_reconfig, NULL); +#endif /* CONFIG_NATIVE_WINDOWS */ + + eloop_run(); + + for (wpa_s = head; wpa_s; wpa_s = wpa_s->next) { + wpa_supplicant_deauthenticate(wpa_s, REASON_DEAUTH_LEAVING); + } + +cleanup: + wpa_s = head; + while (wpa_s) { + struct wpa_supplicant *prev; + wpa_supplicant_deinit(wpa_s); + prev = wpa_s; + wpa_s = wpa_s->next; + free(prev); + } + + eloop_destroy(); + +#ifdef CONFIG_NATIVE_WINDOWS + WSACleanup(); +#endif /* CONFIG_NATIVE_WINDOWS */ + + return exitcode; +} -- cgit v1.1