/*
 * WPA Supplicant
 * Copyright (c) 2003-2004, Jouni Malinen <jkmaline@cc.hut.fi>
 *
 * 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 <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
#include <string.h>
#include <sys/time.h>
#include <time.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <netinet/in.h>

#include "common.h"
#include "md5.h"
#include "sha1.h"
#include "rc4.h"
#include "aes_wrap.h"
#include "wpa.h"
#include "driver.h"
#include "eloop.h"
#include "wpa_supplicant.h"
#include "config.h"
#include "l2_packet.h"

static const char *wpa_supplicant_version =
"wpa_supplicant v0.0 - Copyright (c) 2003-2004, Jouni Malinen "
"<jkmaline@cc.hut.fi>";

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";

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 th 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";

struct wpa_ie_data {
	int pairwise_cipher;
	int group_cipher;
	int key_mgmt;
	int capabilities;
};

struct wpa_ptk {
	u8 mic_key[16]; /* EAPOL-Key MIC Key (MK) */
	u8 encr_key[16]; /* EAPOL-Key Encryption Key (EK) */
	u8 tk1[16]; /* Temporal Key 1 (TK1) */
	union {
		u8 tk2[16]; /* Temporal Key 2 (TK2) */
		struct {
			u8 tx_mic_key[8];
			u8 rx_mic_key[8];
		} auth;
	} u;
} __attribute__ ((packed));


struct wpa_supplicant {
	struct l2_packet_data *l2;
	unsigned char own_addr[ETH_ALEN];
	char ifname[20];
	int dot1x_s; /* socket for connection to Xsupplicant */

	u8 pmk[PMK_LEN];
	u8 counter[WPA_NONCE_LEN];
	u8 snonce[WPA_NONCE_LEN];
	u8 anonce[WPA_NONCE_LEN]; /* ANonce from the last 1/4 msg */
	struct wpa_ptk ptk, tptk;
	int ptk_set, tptk_set;
	char *confname;
	struct wpa_ssid *ssid;
	u8 request_counter[WPA_REPLAY_COUNTER_LEN];
	int countermeasures;
	time_t last_michael_mic_error;
	u8 rx_replay_counter[WPA_REPLAY_COUNTER_LEN];
	int rx_replay_counter_set;
	u8 bssid[ETH_ALEN];
	u8 *ap_wpa_ie;
	size_t ap_wpa_ie_len;
	u8 *assoc_wpa_ie;
	size_t assoc_wpa_ie_len;

	/* Selected configuration (based on Beacon/ProbeResp WPA IE) */
	int pairwise_cipher;
	int group_cipher;
	int key_mgmt;

	void *events_priv; /* private data used by wpa_driver_events */

	struct wpa_ssid *prev_scan_ssid; /* previously scanned SSID;
					  * NULL = not yet initialized (start
					  * with broadcast SSID)
					  * BROADCAST_SSID_SCAN = broadcast
					  * SSID was used in the previous scan
					  */
#define BROADCAST_SSID_SCAN ((struct wpa_ssid *) 1)

	struct wpa_driver_ops *driver;
};


const u8 GENERIC_INFO_ELEM = 0xdd;
const int WPA_SELECTOR_LEN = 4;
static const u8 WPA_OUI_TYPE[] = { 0x00, 0x50, 0xf2, 1 };
static const u16 WPA_VERSION = 1;
static const u8 WPA_AUTH_KEY_MGMT_UNSPEC_802_1X[] = { 0x00, 0x50, 0xf2, 1 };
static const u8 WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X[] = { 0x00, 0x50, 0xf2, 2 };
static const u8 WPA_CIPHER_SUITE_NONE[] = { 0x00, 0x50, 0xf2, 0 };
static const u8 WPA_CIPHER_SUITE_WEP40[] = { 0x00, 0x50, 0xf2, 1 };
static const u8 WPA_CIPHER_SUITE_TKIP[] = { 0x00, 0x50, 0xf2, 2 };
static const u8 WPA_CIPHER_SUITE_WRAP[] = { 0x00, 0x50, 0xf2, 3 };
static const u8 WPA_CIPHER_SUITE_CCMP[] = { 0x00, 0x50, 0xf2, 4 };
static const u8 WPA_CIPHER_SUITE_WEP104[] = { 0x00, 0x50, 0xf2, 5 };

/* WPA IE version 1
 * 00-50-f2:1 (OUI:OUI type)
 * 0x01 0x00 (version; little endian)
 * (all following fields are optional:)
 * Group Suite Selector (4 octets) (default: TKIP)
 * Pairwise Suite Count (2 octets, little endian) (default: 1)
 * Pairwise Suite List (4 * n octets) (default: TKIP)
 * Authenticated Key Management Suite Count (2 octets, little endian)
 *    (default: 1)
 * Authenticated Key Management Suite List (4 * n octets)
 *    (default: unspec 802.1x)
 * WPA Capabilities (2 octets, little endian) (default: 0)
 */

struct wpa_ie_hdr {
	u8 elem_id;
	u8 len;
	u8 oui[3];
	u8 oui_type;
	u16 version;
} __attribute__ ((packed));


enum {
	REASON_UNSPECIFIED = 1,
	REASON_DEAUTH_LEAVING = 3,
	REASON_INVALID_IE = 13,
	REASON_MICHAEL_MIC_FAILURE = 14,
	REASON_4WAY_HANDSHAKE_TIMEOUT = 15,
	REASON_GROUP_KEY_UPDATE_TIMEOUT = 16,
	REASON_IE_IN_4WAY_DIFFERS = 17,
	REASON_GROUP_CIPHER_NOT_VALID = 18,
	REASON_PAIRWISE_CIPHER_NOT_VALID = 19,
	REASON_AKMP_NOT_VALID = 20,
	REASON_UNSUPPORTED_RSN_IE_VERSION = 21,
	REASON_INVALID_RSN_IE_CAPAB = 22,
	REASON_IEEE_802_1X_AUTH_FAILED = 23,
	REASON_CIPHER_SUITE_REJECTED = 24,
};


static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx);
static void wpa_supplicant_scan_results(struct wpa_supplicant *wpa_s);
static void wpa_supplicant_disassociate(struct wpa_supplicant *wpa_s,
					int reason_code);



static int wpa_debug_level = MSG_INFO;

void wpa_printf(int level, char *fmt, ...)
{
	va_list ap;

	va_start(ap, fmt);
	if (level >= wpa_debug_level) {
		vprintf(fmt, ap);
		printf("\n");
	}
	va_end(ap);
}


void wpa_hexdump(int level, const char *title, const u8 *buf, size_t len)
{
	size_t i;
	if (level < wpa_debug_level)
		return;
	printf("%s - hexdump(len=%d):", title, len);
	for (i = 0; i < len; i++)
		printf(" %02x", buf[i]);
	printf("\n");
}


static 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;
}


static int wpa_selector_to_bitfield(u8 *s)
{
	if (memcmp(s, WPA_CIPHER_SUITE_NONE, WPA_SELECTOR_LEN) == 0)
		return WPA_CIPHER_NONE;
	if (memcmp(s, WPA_CIPHER_SUITE_WEP40, WPA_SELECTOR_LEN) == 0)
		return WPA_CIPHER_WEP40;
	if (memcmp(s, WPA_CIPHER_SUITE_TKIP, WPA_SELECTOR_LEN) == 0)
		return WPA_CIPHER_TKIP;
	if (memcmp(s, WPA_CIPHER_SUITE_CCMP, WPA_SELECTOR_LEN) == 0)
		return WPA_CIPHER_CCMP;
	if (memcmp(s, WPA_CIPHER_SUITE_WEP104, WPA_SELECTOR_LEN) == 0)
		return WPA_CIPHER_WEP104;
	return 0;
}


static int wpa_key_mgmt_to_bitfield(u8 *s)
{
	if (memcmp(s, WPA_AUTH_KEY_MGMT_UNSPEC_802_1X, WPA_SELECTOR_LEN) == 0)
		return WPA_KEY_MGMT_IEEE8021X;
	if (memcmp(s, WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X, WPA_SELECTOR_LEN) ==
	    0)
		return WPA_KEY_MGMT_PSK;
	return 0;
}


static int wpa_parse_wpa_ie(struct wpa_supplicant *wpa_s, u8 *wpa_ie,
			    size_t wpa_ie_len, struct wpa_ie_data *data)
{
	struct wpa_ie_hdr *hdr;
	u8 *pos;
	int left;
	int i, count;

	/* TODO: IEEE 802.11i changes the defaults to CCMP instead of TKIP */
	data->pairwise_cipher = WPA_CIPHER_TKIP;
	data->group_cipher = WPA_CIPHER_TKIP;
	data->key_mgmt = WPA_KEY_MGMT_IEEE8021X;

	if (wpa_ie_len < sizeof(struct wpa_ie_hdr))
		return -1;

	hdr = (struct wpa_ie_hdr *) wpa_ie;

	if (hdr->elem_id != GENERIC_INFO_ELEM ||
	    hdr->len != wpa_ie_len - 2 ||
	    memcmp(&hdr->oui, WPA_OUI_TYPE, WPA_SELECTOR_LEN) != 0 ||
	    le_to_host16(hdr->version) != WPA_VERSION) {
		return -1;
	}

	pos = (u8 *) (hdr + 1);
	left = wpa_ie_len - sizeof(*hdr);

	if (left >= WPA_SELECTOR_LEN) {
		data->group_cipher = wpa_selector_to_bitfield(pos);
		pos += WPA_SELECTOR_LEN;
		left -= WPA_SELECTOR_LEN;
	} else if (left > 0)
		  return -1;

	if (left >= 2) {
		data->pairwise_cipher = 0;
		count = pos[0] | (pos[1] << 8);
		pos += 2;
		left -= 2;
		if (count == 0 || left < count * WPA_SELECTOR_LEN)
			return -1;
		for (i = 0; i < count; i++) {
			data->pairwise_cipher |= wpa_selector_to_bitfield(pos);
			pos += WPA_SELECTOR_LEN;
			left -= WPA_SELECTOR_LEN;
		}
	} else if (left == 1)
		return -1;

	if (left >= 2) {
		data->key_mgmt = 0;
		count = pos[0] | (pos[1] << 8);
		pos += 2;
		left -= 2;
		if (count == 0 || left < count * WPA_SELECTOR_LEN)
			return -1;
		for (i = 0; i < count; i++) {
			data->key_mgmt |= wpa_key_mgmt_to_bitfield(pos);
			pos += WPA_SELECTOR_LEN;
			left -= WPA_SELECTOR_LEN;
		}
	} else if (left == 1)
		return -1;

	if (left >= 2) {
		data->capabilities = pos[0] | (pos[1] < 8);
		pos += 2;
		left -= 2;
	}

	if (left > 0) {
		return -1;
	}

	return 0;
}


static int wpa_gen_wpa_ie(struct wpa_supplicant *wpa_s, u8 *wpa_ie)
{
	u8 *pos;
	struct wpa_ie_hdr *hdr;

	hdr = (struct wpa_ie_hdr *) wpa_ie;
	hdr->elem_id = GENERIC_INFO_ELEM;
	memcpy(&hdr->oui, WPA_OUI_TYPE, WPA_SELECTOR_LEN);
	hdr->version = host_to_le16(WPA_VERSION);
	pos = (u8 *) (hdr + 1);

	if (wpa_s->group_cipher == WPA_CIPHER_CCMP) {
		memcpy(pos, WPA_CIPHER_SUITE_CCMP, WPA_SELECTOR_LEN);
	} else if (wpa_s->group_cipher == WPA_CIPHER_TKIP) {
		memcpy(pos, WPA_CIPHER_SUITE_TKIP, WPA_SELECTOR_LEN);
	} else if (wpa_s->group_cipher == WPA_CIPHER_WEP104) {
		memcpy(pos, WPA_CIPHER_SUITE_WEP104, WPA_SELECTOR_LEN);
	} else if (wpa_s->group_cipher == WPA_CIPHER_WEP40) {
		memcpy(pos, WPA_CIPHER_SUITE_WEP40, WPA_SELECTOR_LEN);
	} else {
		wpa_printf(MSG_WARNING, "Invalid group cipher (%d).",
			   wpa_s->group_cipher);
		return -1;
	}
	pos += WPA_SELECTOR_LEN;

	*pos++ = 1;
	*pos++ = 0;
	if (wpa_s->pairwise_cipher == WPA_CIPHER_CCMP) {
		memcpy(pos, WPA_CIPHER_SUITE_CCMP, WPA_SELECTOR_LEN);
	} else if (wpa_s->pairwise_cipher == WPA_CIPHER_TKIP) {
		memcpy(pos, WPA_CIPHER_SUITE_TKIP, WPA_SELECTOR_LEN);
	} else if (wpa_s->pairwise_cipher == WPA_CIPHER_NONE) {
		memcpy(pos, WPA_CIPHER_SUITE_NONE, WPA_SELECTOR_LEN);
	} else {
		wpa_printf(MSG_WARNING, "Invalid pairwise cipher (%d).",
			   wpa_s->pairwise_cipher);
		return -1;
	}
	pos += WPA_SELECTOR_LEN;

	*pos++ = 1;
	*pos++ = 0;
	if (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X) {
		memcpy(pos, WPA_AUTH_KEY_MGMT_UNSPEC_802_1X, WPA_SELECTOR_LEN);
	} else if (wpa_s->key_mgmt == WPA_KEY_MGMT_PSK) {
		memcpy(pos, WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X,
		       WPA_SELECTOR_LEN);
	} else {
		wpa_printf(MSG_WARNING, "Invalid key management type (%d).",
			   wpa_s->key_mgmt);
		return -1;
	}
	pos += WPA_SELECTOR_LEN;

	/* WPA Capabilities; use defaults, so no need to include it */
    *pos++ = 0;
    *pos++ = 0;

	hdr->len = (pos - wpa_ie) - 2;

	return pos - wpa_ie;
}


static void wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s,
				    int sec, int usec)
{
	eloop_cancel_timeout(wpa_supplicant_scan, wpa_s, NULL);
	eloop_register_timeout(sec, usec, wpa_supplicant_scan, wpa_s, NULL);
}


static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
{
	l2_packet_deinit(wpa_s->l2);
	wpa_s->l2 = NULL;

	if (wpa_s->dot1x_s > -1)
		close(wpa_s->dot1x_s);

	wpa_config_free(wpa_s->ssid);
	wpa_s->ssid = 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->confname);
	wpa_s->confname = NULL;
}


static void wpa_pmk_to_ptk(u8 *pmk, u8 *addr1, u8 *addr2,
			   u8 *nonce1, u8 *nonce2, u8 *ptk, size_t ptk_len)
{
	u8 data[2 * ETH_ALEN + 2 * 32];

	/* PTK = PRF-X(PMK, "Pairwise key expansion",
	 *             Min(AA, SA) || Max(AA, SA) ||
	 *             Min(ANonce, SNonce) || Max(ANonce, SNonce)) */

	if (memcmp(addr1, addr2, ETH_ALEN) < 0) {
		memcpy(data, addr1, ETH_ALEN);
		memcpy(data + ETH_ALEN, addr2, ETH_ALEN);
	} else {
		memcpy(data, addr2, ETH_ALEN);
		memcpy(data + ETH_ALEN, addr1, ETH_ALEN);
	}

	if (memcmp(nonce1, nonce2, 32) < 0) {
		memcpy(data + 2 * ETH_ALEN, nonce1, 32);
		memcpy(data + 2 * ETH_ALEN + 32, nonce2, 32);
	} else {
		memcpy(data + 2 * ETH_ALEN, nonce2, 32);
		memcpy(data + 2 * ETH_ALEN + 32, nonce1, 32);
	}

	sha1_prf(pmk, 32, "Pairwise key expansion", data, sizeof(data),
		 ptk, ptk_len);

	wpa_hexdump(MSG_DEBUG, "PMK", pmk, 32);
	wpa_hexdump(MSG_DEBUG, "PTK", ptk, ptk_len);
}


static struct wpa_ssid * wpa_supplicant_get_ssid(struct wpa_supplicant *wpa_s)
{
	struct wpa_ssid *entry;
	u8 ssid[MAX_SSID_LEN];
	int ssid_len;
	u8 bssid[ETH_ALEN];

	ssid_len = wpa_s->driver->get_ssid(wpa_s->ifname, ssid);
	if (ssid_len < 0) {
		wpa_printf(MSG_WARNING, "Could not read SSID from driver.");
		return NULL;
	}

	if (wpa_s->driver->get_bssid(wpa_s->ifname, bssid) < 0) {
		wpa_printf(MSG_WARNING, "Could not read BSSID from driver.");
		return NULL;
	}

	entry = wpa_s->ssid;
	while (entry) {
		if (ssid_len == entry->ssid_len &&
		    memcmp(ssid, entry->ssid, ssid_len) == 0 &&
		    (!entry->bssid_set ||
		     memcmp(bssid, entry->bssid, ETH_ALEN) == 0))
			return entry;
		entry = entry->next;
	}

	return NULL;
}


static void wpa_eapol_key_mic(u8 *key, int ver, u8 *buf, size_t len, u8 *mic)
{
	if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4) {
		hmac_md5(key, 16, buf, len, mic);
	} else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
		u8 hash[SHA1_MAC_LEN];
		hmac_sha1(key, 16, buf, len, hash);
		memcpy(mic, hash, MD5_MAC_LEN);
	}
}


static void wpa_supplicant_key_request(struct wpa_supplicant *wpa_s,
				       int error, int pairwise)
{
	int rlen;
	struct ieee802_1x_hdr *hdr;
	struct wpa_eapol_key *reply;
	unsigned char *rbuf;
	struct l2_ethhdr *ethhdr;
	int key_info, ver;
	u8 bssid[ETH_ALEN];

	if (wpa_s->pairwise_cipher == WPA_CIPHER_CCMP)
		ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES;
	else
		ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4;

	if (wpa_s->driver->get_bssid(wpa_s->ifname, bssid) < 0) {
		wpa_printf(MSG_WARNING, "Failed to read BSSID for EAPOL-Key "
			   "request");
		return;
	}

	rlen = sizeof(*ethhdr) + sizeof(*hdr) + sizeof(*reply);
	rbuf = malloc(rlen);
	if (rbuf == NULL)
		return;

	memset(rbuf, 0, rlen);
	ethhdr = (struct l2_ethhdr *) rbuf;
	memcpy(ethhdr->h_dest, bssid, 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 = EAPOL_VERSION;
	hdr->type = IEEE802_1X_TYPE_EAPOL_KEY;
	hdr->length = htons(sizeof(*reply));

	reply = (struct wpa_eapol_key *) (hdr + 1);
	reply->type = EAPOL_KEY_TYPE_WPA;
	key_info = WPA_KEY_INFO_REQUEST | ver;
	if (wpa_s->ptk_set)
		key_info |= WPA_KEY_INFO_MIC;
	if (error)
		key_info |= WPA_KEY_INFO_ERROR;
	if (pairwise)
		key_info |= WPA_KEY_INFO_KEY_TYPE;
	reply->key_info = host_to_be16(key_info);
	reply->key_length = 0;
	memcpy(reply->replay_counter, wpa_s->request_counter,
	       WPA_REPLAY_COUNTER_LEN);
	inc_byte_array(wpa_s->request_counter, WPA_REPLAY_COUNTER_LEN);

	reply->key_data_length = host_to_be16(0);

	if (key_info & WPA_KEY_INFO_MIC) {
		wpa_eapol_key_mic(wpa_s->ptk.mic_key, ver, (u8 *) hdr,
			 rlen - sizeof(*ethhdr), reply->key_mic);
	}

	wpa_printf(MSG_INFO, "Sending EAPOL-Key Request (error=%d "
		   "pairwise=%d ptk_set=%d len=%d)",
		   error, pairwise, wpa_s->ptk_set, rlen);
	wpa_hexdump(MSG_MSGDUMP, "EAPOL-Key", rbuf, rlen);
	l2_packet_send(wpa_s->l2, rbuf, rlen);
	free(rbuf);
}


static void wpa_supplicant_process_1_of_4(struct wpa_supplicant *wpa_s,
					  unsigned char *src_addr,
					  struct wpa_eapol_key *key, int ver)
{
	int rlen;
	struct ieee802_1x_hdr *hdr;
	struct wpa_eapol_key *reply;
	unsigned char *rbuf;
	struct l2_ethhdr *ethhdr;
	struct wpa_ssid *ssid;
	struct wpa_ptk *ptk;
	u8 buf[8], wpa_ie_buf[80], *wpa_ie;
	int wpa_ie_len;

	wpa_printf(MSG_DEBUG, "RX message 1 of 4-Way Handshake from " MACSTR
		   " (ver=%d)", MAC2STR(src_addr), ver);

	ssid = wpa_supplicant_get_ssid(wpa_s);
	if (ssid == NULL) {
		wpa_printf(MSG_WARNING, "No SSID info found (msg 1 of 4).");
		return;
	}

	if (wpa_s->assoc_wpa_ie) {
		/* The driver reported a WPA IE that may be different from the
		 * one that the Supplicant would use. Message 2/4 has to use
		 * the exact copy of the WPA IE from the Association Request,
		 * so use the value reported by the driver. */
		wpa_ie = wpa_s->assoc_wpa_ie;
		wpa_ie_len = wpa_s->assoc_wpa_ie_len;
	} else {
		wpa_ie = wpa_ie_buf;
		wpa_ie_len = wpa_gen_wpa_ie(wpa_s, wpa_ie);
		if (wpa_ie_len < 0) {
			wpa_printf(MSG_WARNING, "Failed to generate WPA IE "
				   "(msg 1 of 4).");
			return;
		}
	}

	rlen = sizeof(*ethhdr) + sizeof(*hdr) + sizeof(*reply) + wpa_ie_len;
	rbuf = malloc(rlen);
	if (rbuf == NULL)
		return;

	memset(rbuf, 0, rlen);
	ethhdr = (struct l2_ethhdr *) rbuf;
	memcpy(ethhdr->h_dest, src_addr, 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 = EAPOL_VERSION;
	hdr->type = IEEE802_1X_TYPE_EAPOL_KEY;
	hdr->length = htons(sizeof(*reply) + wpa_ie_len);

	reply = (struct wpa_eapol_key *) (hdr + 1);
	reply->type = EAPOL_KEY_TYPE_WPA;
	reply->key_info = host_to_be16(ver | WPA_KEY_INFO_KEY_TYPE |
				       WPA_KEY_INFO_MIC);
	reply->key_length = key->key_length;
	memcpy(reply->replay_counter, key->replay_counter,
	       WPA_REPLAY_COUNTER_LEN);

	reply->key_data_length = host_to_be16(wpa_ie_len);
	memcpy(reply->key_data, wpa_ie, wpa_ie_len);

	memcpy(wpa_s->snonce, wpa_s->counter, WPA_NONCE_LEN);
	inc_byte_array(wpa_s->counter, WPA_NONCE_LEN);
	memcpy(reply->key_nonce, wpa_s->snonce, WPA_NONCE_LEN);
	ptk = &wpa_s->tptk;
	memcpy(wpa_s->anonce, key->key_nonce, WPA_NONCE_LEN);
	wpa_pmk_to_ptk(wpa_s->pmk, wpa_s->own_addr, src_addr,
		       wpa_s->snonce, key->key_nonce,
		       (u8 *) ptk, sizeof(*ptk));
	/* Supplicant: swap tx/rx Mic keys */
	memcpy(buf, ptk->u.auth.tx_mic_key, 8);
	memcpy(ptk->u.auth.tx_mic_key, ptk->u.auth.rx_mic_key, 8);
	memcpy(ptk->u.auth.rx_mic_key, buf, 8);
	wpa_s->tptk_set = 1;
	wpa_eapol_key_mic(wpa_s->tptk.mic_key, ver, (u8 *) hdr,
			  rlen - sizeof(*ethhdr), reply->key_mic);
	wpa_hexdump(MSG_DEBUG, "EAPOL-Key MIC", reply->key_mic, 16);

	wpa_printf(MSG_DEBUG, "Sending EAPOL-Key 2/4");
	wpa_hexdump(MSG_MSGDUMP, "TX EAPOL-Key 2/4", rbuf, rlen);
	l2_packet_send(wpa_s->l2, rbuf, rlen);
	free(rbuf);
}


static void wpa_supplicant_process_3_of_4(struct wpa_supplicant *wpa_s,
					  unsigned char *src_addr,
					  struct wpa_eapol_key *key,
					  int extra_len, int ver)
{
	int rlen;
	struct ieee802_1x_hdr *hdr;
	struct wpa_eapol_key *reply;
	unsigned char *rbuf;
	struct l2_ethhdr *ethhdr;
	int key_info, ie_len, keylen;

	wpa_printf(MSG_DEBUG, "RX message 3 of 4-Way Handshake from " MACSTR
		   " (ver=%d)", MAC2STR(src_addr), ver);

	ie_len = be_to_host16(key->key_data_length);
	if (ie_len > extra_len) {
		wpa_printf(MSG_INFO, "Truncated EAPOL-Key packet: "
			   "ie_len=%d > extra_len=%d", ie_len, extra_len);
		return;
	}

	if (wpa_s->ap_wpa_ie &&
	    (wpa_s->ap_wpa_ie_len != ie_len ||
	     memcmp(wpa_s->ap_wpa_ie, key->key_data, ie_len) != 0)) {
		wpa_printf(MSG_WARNING, "WPA IE in 3/4 msg does not match with"
			   " WPA IE in Beacon/ProbeResp (src=" MACSTR ")",
			   MAC2STR(src_addr));
		wpa_hexdump(MSG_INFO, "WPA IE in Beacon/ProbeResp",
				wpa_s->ap_wpa_ie, wpa_s->ap_wpa_ie_len);
		wpa_hexdump(MSG_INFO, "WPA IE in 3/4 msg",
			    key->key_data, ie_len);
		wpa_supplicant_disassociate(wpa_s, REASON_IE_IN_4WAY_DIFFERS);
		wpa_supplicant_req_scan(wpa_s, 0, 0);
		return;
	}

	if (memcmp(wpa_s->anonce, key->key_nonce, WPA_NONCE_LEN) != 0) {
		wpa_printf(MSG_WARNING, "ANonce from message 1 of 4-Way "
			   "Handshake differs from 3 of 4-Way Handshake - drop"
			   " packet (src=" MACSTR ")", MAC2STR(src_addr));
		return;
	}

	keylen = be_to_host16(key->key_length);
	switch (wpa_s->pairwise_cipher) {
	case WPA_CIPHER_CCMP:
		if (keylen != 16) {
			wpa_printf(MSG_WARNING, "Invalid CCMP key length %d "
				   "(src=" MACSTR ")",
				   keylen, MAC2STR(src_addr));
			return;
		}
		break;
	case WPA_CIPHER_TKIP:
		if (keylen != 32) {
			wpa_printf(MSG_WARNING, "Invalid TKIP key length %d "
				   "(src=" MACSTR ")",
				   keylen, MAC2STR(src_addr));
			return;
		}
		break;
	}

	rlen = sizeof(*ethhdr) + sizeof(*hdr) + sizeof(*reply);
	rbuf = malloc(rlen);
	if (rbuf == NULL)
		return;

	memset(rbuf, 0, rlen);
	ethhdr = (struct l2_ethhdr *) rbuf;
	memcpy(ethhdr->h_dest, src_addr, 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 = EAPOL_VERSION;
	hdr->type = IEEE802_1X_TYPE_EAPOL_KEY;
	hdr->length = htons(sizeof(*reply));

	key_info = be_to_host16(key->key_info);
	reply = (struct wpa_eapol_key *) (hdr + 1);
	reply->type = EAPOL_KEY_TYPE_WPA;
	reply->key_info = host_to_be16(ver | WPA_KEY_INFO_KEY_TYPE |
				       WPA_KEY_INFO_MIC |
				       (key_info & WPA_KEY_INFO_SECURE));
	reply->key_length = key->key_length;
	memcpy(reply->replay_counter, key->replay_counter,
	       WPA_REPLAY_COUNTER_LEN);

	reply->key_data_length = host_to_be16(0);

	memcpy(reply->key_nonce, wpa_s->snonce, WPA_NONCE_LEN);
	wpa_eapol_key_mic(wpa_s->ptk.mic_key, ver, (u8 *) hdr,
			  rlen - sizeof(*ethhdr), reply->key_mic);

	wpa_printf(MSG_DEBUG, "Sending EAPOL-Key 4/4");
	wpa_hexdump(MSG_MSGDUMP, "EAPOL-Key", rbuf, rlen);
	l2_packet_send(wpa_s->l2, rbuf, rlen);
	free(rbuf);

	if (key_info & WPA_KEY_INFO_INSTALL) {
		int alg, keylen, rsclen;
		wpa_printf(MSG_DEBUG, "Installing PTK to the driver.");
		switch (wpa_s->pairwise_cipher) {
		case WPA_CIPHER_CCMP:
			alg = WPA_ALG_CCMP;
			keylen = 16;
			rsclen = 6;
			break;
		case WPA_CIPHER_TKIP:
			alg = WPA_ALG_TKIP;
			keylen = 32;
			rsclen = 6;
			break;
		case WPA_CIPHER_NONE:
			wpa_printf(MSG_DEBUG, "Pairwise Cipher Suite: NONE - "
				   "do not use pairwise keys");
			return;
		default:
			wpa_printf(MSG_WARNING, "Unsupported pairwise cipher "
				   "%d", wpa_s->pairwise_cipher);
			return;
		}
		wpa_hexdump(MSG_DEBUG, "RSC", key->key_rsc, rsclen);
		if (wpa_s->driver->set_key(wpa_s->ifname, alg, src_addr,
					   0, 1, key->key_rsc, rsclen,
					   (u8 *) &wpa_s->ptk.tk1, keylen) < 0)
		{
			wpa_printf(MSG_WARNING, "Failed to set PTK to the "
				   "driver.");
		}
	}
}


static void wpa_supplicant_process_1_of_2(struct wpa_supplicant *wpa_s,
					  unsigned char *src_addr,
					  struct wpa_eapol_key *key,
					  int extra_len, int ver)
{
	int rlen;
	struct ieee802_1x_hdr *hdr;
	struct wpa_eapol_key *reply;
	unsigned char *rbuf;
	struct l2_ethhdr *ethhdr;
	int key_info, keylen, keydatalen, maxkeylen, keyidx, key_rsc_len = 0;
	int alg;
	u8 ek[32], tmpbuf[8], gtk[32];

	wpa_printf(MSG_DEBUG, "RX message 1 of Group Key Handshake from "
		   MACSTR " (ver=%d)", MAC2STR(src_addr), ver);

	key_info = be_to_host16(key->key_info);
	keylen = be_to_host16(key->key_length);
	maxkeylen = keydatalen = be_to_host16(key->key_data_length);
	if (keydatalen > extra_len) {
		wpa_printf(MSG_INFO, "Truncated EAPOL-Key packet: "
			   "key_data_length=%d > extra_len=%d",
			   keydatalen, extra_len);
		return;
	}
	if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES)
		maxkeylen -= 8;
	switch (wpa_s->group_cipher) {
	case WPA_CIPHER_CCMP:
		if (keylen != 16 || maxkeylen < 16) {
			wpa_printf(MSG_WARNING, "Unsupported CCMP Group Cipher"
				   " key length %d (%d).", keylen, maxkeylen);
			return;
		}
		key_rsc_len = 6;
		alg = WPA_ALG_CCMP;
		break;
	case WPA_CIPHER_TKIP:
		if (keylen != 32 || maxkeylen < 32) {
			wpa_printf(MSG_WARNING, "Unsupported TKIP Group Cipher"
				   " key length %d (%d).",
				   keylen, maxkeylen);
			return;
		}
		key_rsc_len = 6;
		alg = WPA_ALG_TKIP;
		break;
	case WPA_CIPHER_WEP104:
		if (keylen != 13 || maxkeylen < 13) {
			wpa_printf(MSG_WARNING, "Unsupported WEP104 Group "
				   "Cipher key length %d (%d).",
				   keylen, maxkeylen);
			return;
		}
		alg = WPA_ALG_WEP;
		break;
	case WPA_CIPHER_WEP40:
		if (keylen != 5 || maxkeylen < 13) {
			wpa_printf(MSG_WARNING, "Unsupported WEP40 Group "
				   "Cipher key length %d (%d).",
				   keylen, maxkeylen);
			return;
		}
		alg = WPA_ALG_WEP;
		break;
	default:
		wpa_printf(MSG_WARNING, "Unsupport Group Cipher %d",
			   wpa_s->group_cipher);
		return;
	}

	keyidx = (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) >>
		WPA_KEY_INFO_KEY_INDEX_SHIFT;
	if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4) {
		memcpy(ek, key->key_iv, 16);
		memcpy(ek + 16, wpa_s->ptk.encr_key, 16);
		rc4_skip(ek, 32, 256, key->key_data, keydatalen);
		memcpy(gtk, key->key_data, keylen);
	} else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
		if (keydatalen % 8) {
			wpa_printf(MSG_WARNING, "Unsupported AES-WRAP len %d",
				   keydatalen);
			return;
		}
		if (aes_unwrap(wpa_s->ptk.encr_key, maxkeylen / 8,
			       key->key_data, gtk)) {
			wpa_printf(MSG_WARNING, "AES unwrap failed - could not"
				   " decrypt GTK");
			return;
		}
	}
	wpa_hexdump(MSG_DEBUG, "Group Key", gtk, keylen);
	wpa_printf(MSG_DEBUG, "Installing GTK to the driver (keyidx=%d).",
		   keyidx);
	wpa_hexdump(MSG_DEBUG, "RSC", key->key_rsc, 6);
	if (wpa_s->group_cipher == WPA_CIPHER_TKIP) {
		/* Swap Tx/Rx keys for Michael MIC */
		memcpy(tmpbuf, gtk + 16, 8);
		memcpy(gtk + 16, gtk + 24, 8);
		memcpy(gtk + 24, tmpbuf, 8);
	}
	if (wpa_s->pairwise_cipher == WPA_CIPHER_NONE) {
		if (wpa_s->driver->set_key(wpa_s->ifname, alg,
					   "\xff\xff\xff\xff\xff\xff",
					   keyidx, 1, key->key_rsc,
					   key_rsc_len, gtk, keylen) < 0)
			wpa_printf(MSG_WARNING, "Failed to set GTK to the "
				   "driver (Group only).");
	} else if (wpa_s->driver->set_key(wpa_s->ifname, alg, src_addr,
					  keyidx,
					  !!(key_info & WPA_KEY_INFO_TXRX),
					  key->key_rsc, key_rsc_len,
					  gtk, keylen) < 0) {
		wpa_printf(MSG_WARNING, "Failed to set GTK to the driver.");
	}

	rlen = sizeof(*ethhdr) + sizeof(*hdr) + sizeof(*reply);
	rbuf = malloc(rlen);
	if (rbuf == NULL)
		return;

	memset(rbuf, 0, rlen);
	ethhdr = (struct l2_ethhdr *) rbuf;
	memcpy(ethhdr->h_dest, src_addr, 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 = EAPOL_VERSION;
	hdr->type = IEEE802_1X_TYPE_EAPOL_KEY;
	hdr->length = htons(sizeof(*reply));

	reply = (struct wpa_eapol_key *) (hdr + 1);
	reply->type = EAPOL_KEY_TYPE_WPA;
	reply->key_info =
		host_to_be16(ver | WPA_KEY_INFO_MIC | WPA_KEY_INFO_SECURE |
			     (key_info & WPA_KEY_INFO_KEY_INDEX_MASK));
	reply->key_length = key->key_length;
	memcpy(reply->replay_counter, key->replay_counter,
		WPA_REPLAY_COUNTER_LEN);

	reply->key_data_length = host_to_be16(0);

	wpa_eapol_key_mic(wpa_s->ptk.mic_key, ver, (u8 *) hdr,
			  rlen - sizeof(*ethhdr), reply->key_mic);

	wpa_printf(MSG_DEBUG, "Sending EAPOL-Key 2/2");
	wpa_hexdump(MSG_MSGDUMP, "EAPOL-Key", rbuf, rlen);
	l2_packet_send(wpa_s->l2, rbuf, rlen);
	free(rbuf);

	wpa_printf(MSG_INFO, "WPA key negotiation completed with " MACSTR,
		   MAC2STR(src_addr));
	eloop_cancel_timeout(wpa_supplicant_scan, wpa_s, NULL);
}


static void wpa_supplicant_rx_eapol(void *ctx, unsigned char *src_addr,
				    unsigned char *buf, size_t len)
{
	struct wpa_supplicant *wpa_s = ctx;
	int plen;
	struct ieee802_1x_hdr *hdr;
	struct wpa_eapol_key *key;
	int key_info, extra_len, ver;
	u8 bssid[ETH_ALEN];

	wpa_printf(MSG_DEBUG, "RX EAPOL from " MACSTR, MAC2STR(src_addr));

	if (wpa_s->countermeasures) {
		wpa_printf(MSG_INFO, "Countermeasures - dropped EAPOL packet");
		return;
	}

	if (memcmp(wpa_s->bssid, "\x00\x00\x00\x00\x00\x00", ETH_ALEN) == 0) {
		wpa_printf(MSG_WARNING, "EAPOL frame in disassociated state - "
			   "dropped");
		return;
	}

	if (wpa_s->driver->get_bssid(wpa_s->ifname, bssid) < 0 ||
	    memcmp(bssid, src_addr, ETH_ALEN) != 0) {
		wpa_printf(MSG_WARNING, "EAPOL frame from foreign address - "
			   "dropped");
		return;
	}

	hdr = (struct ieee802_1x_hdr *) buf;
	key = (struct wpa_eapol_key *) (hdr + 1);
	if (len < sizeof(*hdr) + sizeof(*key))
		return;
	plen = ntohs(hdr->length);
	wpa_printf(MSG_DEBUG, "IEEE 802.1X: version=%d type=%d length=%d",
		   hdr->version, hdr->type, plen);

	if (hdr->version != EAPOL_VERSION ||
	    hdr->type != IEEE802_1X_TYPE_EAPOL_KEY ||
	    plen > len - sizeof(*hdr) || plen < sizeof(*key))
		return;

	wpa_printf(MSG_DEBUG, "  EAPOL-Key type=%d", key->type);
	if (key->type != EAPOL_KEY_TYPE_WPA)
		return;

	wpa_hexdump(MSG_MSGDUMP, "RX WPA EAPOL-Key", buf, len);
	key_info = be_to_host16(key->key_info);
	ver = key_info & WPA_KEY_INFO_TYPE_MASK;
	if (ver != WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 &&
	    ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
		wpa_printf(MSG_INFO, "Unsupported EAPOL-Key descriptor version"
			   " %d.", ver);
		return;
	}

	if (wpa_s->pairwise_cipher == WPA_CIPHER_CCMP &&
	    ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
		wpa_printf(MSG_INFO, "CCMP is used, but EAPOL-Key descriptor "
			   "version (%d) is not 2.", ver);
		if (wpa_s->group_cipher != WPA_CIPHER_CCMP &&
		    !(key_info & WPA_KEY_INFO_KEY_TYPE)) {
			/* Earlier versions of IEEE 802.11i did not explicitly
			 * require version 2 descriptor for all EAPOL-Key
			 * packets, so allow group keys to use version 1 if
			 * CCMP is not used for them. */
			wpa_printf(MSG_INFO, "Backwards compatibility: allow "
				   "invalid version for non-CCMP group keys");
		} else
			return;
	}

	if (wpa_s->rx_replay_counter_set &&
	    memcmp(key->replay_counter, wpa_s->rx_replay_counter,
		   WPA_REPLAY_COUNTER_LEN) <= 0) {
		wpa_printf(MSG_WARNING, "EAPOL-Key Replay Counter did not "
			   "increase - dropping packet");
		return;
	}

	if (!(key_info & WPA_KEY_INFO_ACK)) {
		wpa_printf(MSG_INFO, "No Ack bit in key_info");
		return;
	}

	if (key_info & WPA_KEY_INFO_REQUEST) {
		wpa_printf(MSG_INFO, "EAPOL-Key with Request bit - dropped");
		return;
	}

	if (key_info & WPA_KEY_INFO_MIC) {
		u8 mic[16];
		int ok = 0;
		memcpy(mic, key->key_mic, 16);
		if (wpa_s->tptk_set) {
			memset(key->key_mic, 0, 16);
			wpa_eapol_key_mic(wpa_s->tptk.mic_key, ver, buf, len,
					  key->key_mic);
			if (memcmp(mic, key->key_mic, 16) != 0) {
				wpa_printf(MSG_WARNING, "Invalid EAPOL-Key MIC"
					   " when using TPTK  - ignoring "
					   "TPTK");
			} else {
				ok = 1;
				wpa_s->tptk_set = 0;
				wpa_s->ptk_set = 1;
				memcpy(&wpa_s->ptk, &wpa_s->tptk,
				       sizeof(wpa_s->ptk));
			}
		}

		if (!ok && wpa_s->ptk_set) {
			memset(key->key_mic, 0, 16);
			wpa_eapol_key_mic(wpa_s->ptk.mic_key, ver, buf, len,
					  key->key_mic);
			if (memcmp(mic, key->key_mic, 16) != 0) {
				wpa_printf(MSG_WARNING, "Invalid EAPOL-Key MIC"
					   " - dropping packet");
				return;
			}
			ok = 1;
		}

		if (!ok) {
			wpa_printf(MSG_WARNING, "Could not verify EAPOL-Key "
				   "MIC - dropping packet");
			return;
		}

		memcpy(wpa_s->rx_replay_counter, key->replay_counter,
		       WPA_REPLAY_COUNTER_LEN);
		wpa_s->rx_replay_counter_set = 1;
	}

	extra_len = len - sizeof(*hdr) - sizeof(*key);
	if (key_info & WPA_KEY_INFO_KEY_TYPE) {
		if (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) {
			wpa_printf(MSG_WARNING, "Ignored EAPOL-Key (Pairwise) "
				   "with non-zero key index");
			return;
		}
		if (key_info & WPA_KEY_INFO_MIC) {
			/* 3/4 4-Way Handshake */
			wpa_supplicant_process_3_of_4(wpa_s, src_addr, key,
						      extra_len, ver);
		} else {
			/* 1/4 4-Way Handshake */
			wpa_supplicant_process_1_of_4(wpa_s, src_addr, key,
						      ver);
		}
	} else {
		if (key_info & WPA_KEY_INFO_MIC) {
			/* 1/2 Group Key Handshake */
			wpa_supplicant_process_1_of_2(wpa_s, src_addr, key,
						      extra_len, ver);
		} else {
			wpa_printf(MSG_WARNING, "EAPOL-Key (Group) without Mic"
				   " bit - dropped\n");
		}
	}
}


static void wpa_clear_keys(struct wpa_supplicant *wpa_s)
{
	wpa_s->driver->set_key(wpa_s->ifname, WPA_ALG_NONE, NULL, 0, 0, NULL,
			       0, NULL, 0);
	wpa_s->driver->set_key(wpa_s->ifname, WPA_ALG_NONE, NULL, 1, 0, NULL,
			       0, NULL, 0);
	wpa_s->driver->set_key(wpa_s->ifname, WPA_ALG_NONE, NULL, 2, 0, NULL,
			       0, NULL, 0);
	wpa_s->driver->set_key(wpa_s->ifname, WPA_ALG_NONE, NULL, 3, 0, NULL,
			       0, NULL, 0);
}


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_s->driver->set_countermeasures(wpa_s->ifname, 0);
		wpa_printf(MSG_INFO, "TKIP countermeasures stopped");
		wpa_supplicant_req_scan(wpa_s, 0, 0);
	}
}


void wpa_supplicant_event(struct wpa_supplicant *wpa_s, wpa_event_type event,
			  union wpa_event_data *data)
{
	int pairwise, l, len;
	time_t now;
	u8 bssid[ETH_ALEN], *p;

	switch (event) {
	case EVENT_ASSOC:
		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;
		if (wpa_s->driver->get_bssid(wpa_s->ifname, bssid) >= 0 &&
		    memcmp(bssid, wpa_s->bssid, ETH_ALEN) != 0) {
			wpa_printf(MSG_DEBUG, "Associated to a new BSS: "
				   "BSSID=" MACSTR, MAC2STR(bssid));
			memcpy(wpa_s->bssid, bssid, ETH_ALEN);
			wpa_clear_keys(wpa_s);
		}
		wpa_supplicant_req_scan(wpa_s, wpa_s->key_mgmt ==
					WPA_KEY_MGMT_IEEE8021X ? 30 : 10, 0);
		break;
	case EVENT_DISASSOC:
		wpa_printf(MSG_DEBUG, "Disconnect event - remove keys");
		wpa_clear_keys(wpa_s);
		wpa_supplicant_req_scan(wpa_s, 3, 0);
		memset(wpa_s->bssid, 0, ETH_ALEN);
		break;
	case EVENT_MICHAEL_MIC_FAILURE:
		wpa_printf(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_printf(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_s->driver->set_countermeasures(wpa_s->ifname, 1);
			wpa_supplicant_disassociate(
				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);
		}
		wpa_s->last_michael_mic_error = now;
		break;
	case EVENT_SCAN_RESULTS:
		wpa_supplicant_scan_results(wpa_s);
		break;
	case EVENT_ASSOCINFO:
		wpa_printf(MSG_DEBUG, "Association info event");
		wpa_hexdump(MSG_DEBUG, "req_ies", data->assoc_info.req_ies,
			    data->assoc_info.req_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 IE, if
		 * present. */
		while (l > (2 + 6)) {
			len = p[1] + 2;
			if ((p[0] == GENERIC_INFO_ELEM) && (p[1] > 6) &&
			    (memcmp(&p[2], "\x00\x50\xF2\x01\x01\x00", 6) ==
			     0)) {
				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);
				break;
			}
			l -= len;
			p += len;
		}
		break;
	default:
		wpa_printf(MSG_INFO, "Unknown event %d", event);
		break;
	}
}


static int wpa_supplicant_init_counter(struct wpa_supplicant *wpa_s)
{
	u8 buf[ETH_ALEN + 8];
	struct timeval tv;
	u32 val;
	u8 key[32];

	if (hostapd_get_rand(key, 32))
		return -1;
	memcpy(buf, wpa_s->own_addr, ETH_ALEN);
	gettimeofday(&tv, NULL);
	val = tv.tv_sec;
	memcpy(buf + ETH_ALEN, &val, 4);
	val = tv.tv_usec;
	memcpy(buf + ETH_ALEN + 4, &val, 4);

	sha1_prf(key, 32, "Init Counter", buf, sizeof(buf),
		 wpa_s->counter, sizeof(wpa_s->counter));
	return 0;
}


static void wpa_supplicant_terminate(int sig, void *eloop_ctx,
				     void *signal_ctx)
{
	wpa_printf(MSG_DEBUG, "Signal %d received - terminating", sig);
	eloop_terminate();
}


static void wpa_supplicant_reconfig(int sig, void *eloop_ctx,
				    void *signal_ctx)
{
	struct wpa_supplicant *wpa_s = eloop_ctx;
	struct wpa_ssid *ssid;
	wpa_printf(MSG_DEBUG, "Signal %d received - reconfiguring", sig);
	if (wpa_s->confname == NULL)
		return;
	ssid = wpa_config_read(wpa_s->confname);
	if (ssid == NULL) {
		wpa_printf(MSG_ERROR, "Failed to parse the configuration file "
			   "'%s' - exiting", wpa_s->confname);
		eloop_terminate();
	}

	wpa_config_free(wpa_s->ssid);
	wpa_s->ssid = ssid;
	wpa_supplicant_req_scan(wpa_s, 0, 0);
	wpa_printf(MSG_DEBUG, "Reconfiguration completed");
}


static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx)
{
	struct wpa_supplicant *wpa_s = eloop_ctx;
	struct wpa_ssid *ssid;

	ssid = wpa_s->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(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_s->driver->scan(wpa_s->ifname, wpa_s,
				ssid ? ssid->ssid : NULL,
				ssid ? ssid->ssid_len : 0)) {
		wpa_printf(MSG_WARNING, "Failed to initiate AP scan.");
	}

	eloop_register_timeout(5, 0, wpa_supplicant_scan, wpa_s, NULL);
}


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:
		return KEY_MGMT_802_1X;
	case WPA_KEY_MGMT_PSK:
	default:
		return KEY_MGMT_PSK;
	}
}


static void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
				     struct wpa_scan_result *bss,
				     struct wpa_ssid *ssid)
{
	u8 wpa_ie[80];
	struct wpa_ie_data ie;
	int sel, wpa_ie_len;

	wpa_printf(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);

	if (wpa_parse_wpa_ie(wpa_s, bss->wpa_ie, bss->wpa_ie_len, &ie)) {
		wpa_printf(MSG_WARNING, "Failed to parse WPA IE for the "
			   "selected BSS.");
		return;
	}

	free(wpa_s->ap_wpa_ie);
	if (bss->wpa_ie_len) {
		wpa_s->ap_wpa_ie = malloc(bss->wpa_ie_len);
		if (wpa_s->ap_wpa_ie) {
			memcpy(wpa_s->ap_wpa_ie, bss->wpa_ie,
			       bss->wpa_ie_len);
			wpa_s->ap_wpa_ie_len = bss->wpa_ie_len;
		}
	}

	sel = ie.group_cipher & ssid->group_cipher;
	if (sel & WPA_CIPHER_CCMP) {
		wpa_s->group_cipher = WPA_CIPHER_CCMP;
	} else if (sel & WPA_CIPHER_TKIP) {
		wpa_s->group_cipher = WPA_CIPHER_TKIP;
	} else if (sel & WPA_CIPHER_WEP104) {
		wpa_s->group_cipher = WPA_CIPHER_WEP104;
	} else if (sel & WPA_CIPHER_WEP40) {
		wpa_s->group_cipher = WPA_CIPHER_WEP40;
	} else {
		wpa_printf(MSG_WARNING, "Failed to select group cipher.");
		return;
	}

	sel = ie.pairwise_cipher & ssid->pairwise_cipher;
	if (sel & WPA_CIPHER_CCMP) {
		wpa_s->pairwise_cipher = WPA_CIPHER_CCMP;
	} else if (sel & WPA_CIPHER_TKIP) {
		wpa_s->pairwise_cipher = WPA_CIPHER_TKIP;
	} else if (sel & WPA_CIPHER_NONE) {
		wpa_s->pairwise_cipher = WPA_CIPHER_NONE;
	} else {
		wpa_printf(MSG_WARNING, "Failed to select pairwise cipher.");
		return;
	}

	sel = ie.key_mgmt & ssid->key_mgmt;
	if (sel & WPA_KEY_MGMT_IEEE8021X) {
		wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X;
	} else if (sel & WPA_KEY_MGMT_PSK) {
		wpa_s->key_mgmt = WPA_KEY_MGMT_PSK;
	} else {
		wpa_printf(MSG_WARNING, "Failed to select authenticated key "
			   "management type.");
		return;
	}

	/* 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;

	wpa_ie_len = wpa_gen_wpa_ie(wpa_s, wpa_ie);
	if (wpa_ie_len < 0) {
		wpa_printf(MSG_WARNING, "Failed to generate WPA IE.");
		return;
	}
	wpa_hexdump(MSG_DEBUG, "Own WPA IE", wpa_ie, wpa_ie_len);

	if (ssid->key_mgmt & WPA_KEY_MGMT_PSK)
		memcpy(wpa_s->pmk, ssid->psk, PMK_LEN);
	else
		memset(wpa_s->pmk, 0, PMK_LEN);

	wpa_clear_keys(wpa_s);
	wpa_s->driver->associate(wpa_s->ifname, bss->bssid,
				 bss->ssid, bss->ssid_len, bss->freq,
				 wpa_ie, wpa_ie_len,
				 cipher_suite2driver(wpa_s->pairwise_cipher),
				 cipher_suite2driver(wpa_s->group_cipher),
				 key_mgmt2driver(wpa_s->key_mgmt));

	wpa_supplicant_req_scan(wpa_s, wpa_s->key_mgmt ==
				WPA_KEY_MGMT_IEEE8021X ? 30 : 10, 0);
}


static void wpa_supplicant_disassociate(struct wpa_supplicant *wpa_s,
					int reason_code)
{
	if (memcmp(wpa_s->bssid, "\x00\x00\x00\x00\x00\x00", ETH_ALEN) != 0) {
		wpa_s->driver->disassociate(wpa_s->ifname, wpa_s->bssid,
					    reason_code);
	}
	wpa_clear_keys(wpa_s);
}


static void wpa_supplicant_scan_results(struct wpa_supplicant *wpa_s)
{
#define SCAN_AP_LIMIT 50
	struct wpa_scan_result results[SCAN_AP_LIMIT];
	int num, i;
	struct wpa_scan_result *bss, *selected = NULL;
	struct wpa_ssid *ssid;

	num = wpa_s->driver->get_scan_results(wpa_s->ifname, results,
					      SCAN_AP_LIMIT);
	wpa_printf(MSG_DEBUG, "Scan results: %d", num);
	if (num < 0)
		return;
	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;
	}

	bss = NULL;
	ssid = NULL;
	for (i = 0; i < num && !selected; i++) {
		bss = &results[i];
		wpa_printf(MSG_DEBUG, "%d: " MACSTR " ssid='%s' "
			   "wpa_ie_len=%d", i, MAC2STR(bss->bssid),
			   wpa_ssid_txt(bss->ssid, bss->ssid_len),
			   bss->wpa_ie_len);
		if (bss->wpa_ie_len == 0)
			continue; /* TODO: add support for non-WPA mode? */

		ssid = wpa_s->ssid;
		while (ssid) {
			struct wpa_ie_data ie;
			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) &&
			    wpa_parse_wpa_ie(wpa_s, bss->wpa_ie,
					     bss->wpa_ie_len, &ie) == 0 &&
			    (ie.pairwise_cipher & ssid->pairwise_cipher) &&
			    (ie.group_cipher & ssid->group_cipher) &&
			    (ie.key_mgmt & ssid->key_mgmt)) {
				selected = bss;
				break;
			}
			ssid = ssid->next;
		}
	}

	if (selected)
		wpa_supplicant_associate(wpa_s, selected, ssid);
	else
		wpa_printf(MSG_DEBUG, "No suitable AP found.");
}


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, "Receive from dot1x (Xsupplicant) socket ==> %d",
		   res);
	if (res < 0) {
		perror("recv");
		return;
	}

	if (res != PMK_LEN) {
		wpa_printf(MSG_WARNING, "Invalid master key length (%d) from "
			   "dot1x", res);
		return;
	}

	wpa_hexdump(MSG_DEBUG, "Master key (dot1x)", buf, PMK_LEN);
	if (wpa_s->key_mgmt & WPA_KEY_MGMT_IEEE8021X) {
		memcpy(wpa_s->pmk, buf, PMK_LEN);
	} else {
		wpa_printf(MSG_INFO, "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;
}


extern struct wpa_driver_ops wpa_driver_hostap_ops; /* driver_hostap.c */
extern struct wpa_driver_ops wpa_driver_prism54_ops; /* driver_prism54.c */
extern struct wpa_driver_ops wpa_driver_hermes_ops; /* driver_hermes.c */

static int wpa_supplicant_set_driver(struct wpa_supplicant *wpa_s,
				     const char *name)
{
	if (name == NULL)
		wpa_s->driver = &wpa_driver_hostap_ops;
	else if (strcmp(name, "hostap") == 0)
		wpa_s->driver = &wpa_driver_hostap_ops;
	else if (strcmp(name, "prism54") == 0)
		wpa_s->driver = &wpa_driver_prism54_ops;
    else if (strcmp(name, "hermes") == 0)
        wpa_s->driver = &wpa_driver_hermes_ops;
	else {
		printf("Unsupported driver '%s'.\n", name);
		return -1;
	}
	return 0;
}


static void usage(void)
{
	printf("%s\n\n%s\n"
	       "usage:\n"
	       "  wpa_supplicant [-BddhLqqvw] -i<ifname> -c<config file> "
	       "[-D<driver>]\n"
	       "\n"
	       "drivers:\n"
	       "  hostap = Host AP driver (Intersil Prism2/2.5/3)\n"
	       "  prism54 = Prism54.org driver (Intersil Prism GT/Duette/"
	       "Indigo)\n"
	       "options:\n"
	       "  -B = run daemon in the background\n"
	       "  -d = increase debugging verbosity (-dd even more)\n"
	       "  -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",
	       wpa_supplicant_version, wpa_supplicant_license);
}


static void license(void)
{
	printf("%s\n\n%s\n",
	       wpa_supplicant_version, wpa_supplicant_full_license);
}


int main(int argc, char *argv[])
{
	struct wpa_supplicant wpa_s;
	char *ifname = NULL;
	int c;
	const char *confname = NULL, *driver = NULL;
	int daemonize = 0, wait_for_interface = 0;

	memset(&wpa_s, 0, sizeof(wpa_s));

	for (;;) {
		c = getopt(argc, argv, "Bc:D:dhi:Lqvw");
		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;
		case 'h':
			usage();
			return -1;
		case 'i':
			ifname = optarg;
			break;
		case 'L':
			license();
			return -1;
		case 'q':
			wpa_debug_level++;
			break;
		case 'v':
			printf("%s\n", wpa_supplicant_version);
			return -1;
		case 'w':
			wait_for_interface++;
			break;
		default:
			usage();
			return -1;
		}
	}

	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.ssid = wpa_config_read(wpa_s.confname);
		if (wpa_s.ssid == NULL) {
			printf("Failed to read configuration file '%s'.\n",
			       wpa_s.confname);
			return 1;
		}
	}

	if (wpa_s.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));

	if (wait_for_interface && daemonize) {
		wpa_printf(MSG_DEBUG, "Daemonize..");
		if (daemon(0, 0)) {
			perror("daemon");
			return -1;
		}
	}

	eloop_init(&wpa_s);

	/* 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.events_priv = wpa_s.driver->events_init(&wpa_s);
	if (wpa_s.events_priv == NULL) {
		fprintf(stderr, "Failed to initialize driver event "
			"processing\n");
		return -1;
	}

	for (;;) {
		wpa_s.l2 = l2_packet_init(wpa_s.ifname,
					  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;
	}

	if (wpa_supplicant_init_counter(&wpa_s))
		return -1;

	if (wpa_s.driver->set_wpa(wpa_s.ifname, 1) < 0) {
		fprintf(stderr, "Failed to enable WPA in the driver.\n");
		return -1;
	}

	wpa_clear_keys(&wpa_s);

	/* Make sure that TKIP countermeasures are not left enabled (could
	 * happen if wpa_supplicant is killed during countermeasures. */
	wpa_s.driver->set_countermeasures(wpa_s.ifname, 0);

	wpa_s.driver->set_drop_unencrypted(wpa_s.ifname, 1);

	eloop_register_timeout(0, 100000, wpa_supplicant_scan, &wpa_s, NULL);

	wpa_supplicant_802_1x_init(&wpa_s);

	if (!wait_for_interface && daemonize) {
		wpa_printf(MSG_DEBUG, "Daemonize..");
		if (daemon(0, 0)) {
			perror("daemon");
			return -1;
		}
	}

	eloop_register_signal(SIGINT, wpa_supplicant_terminate, NULL);
	eloop_register_signal(SIGTERM, wpa_supplicant_terminate, NULL);
	eloop_register_signal(SIGHUP, wpa_supplicant_reconfig, NULL);

	eloop_run();

	wpa_supplicant_disassociate(&wpa_s, REASON_DEAUTH_LEAVING);
	if (wpa_s.driver->set_wpa(wpa_s.ifname, 0) < 0) {
		fprintf(stderr, "Failed to disable WPA in the driver.\n");
	}

	if (wpa_s.events_priv)
		wpa_s.driver->events_deinit(&wpa_s, wpa_s.events_priv);

	wpa_s.driver->set_drop_unencrypted(wpa_s.ifname, 0);
	wpa_s.driver->set_countermeasures(wpa_s.ifname, 0);

	wpa_supplicant_cleanup(&wpa_s);

	eloop_destroy();

	return 0;
}
