Accumulative patch from commit 932659696e2755bb1ecd6a27e1968fd27eef4948

9326596 nl80211: Remove unused WPA_DRIVER_FLAGS_MULTI_CHANNEL_CONCURRENT
0d08efa P2P: Use the number of concurrent channels in P2P flows
a21816a Use wpa_drv_shared_freq() if get_radio_name() is not supported
53c5dfc Change share_vif_oper_freq() to handle multiple freqs
4752147 nl80211: Report the number of concurrent support channels
d7df0fa Clean up wpa_supplicant_event() with deauth/disassoc helper functions
fd9f170 Remove unnecessary nested ifdef CONFIG_AP
13adc57 IBSS RSN: Add peer restart detection
b21990b nl80211: Register for AUTH frames when joining an IBSS network
c91f796 nl80211: Support not specifying the frame frequency
ec384c5 IBSS RSN: Fix disconnect() with internal SME
ed07764 nl80211: Remove redundant assignment of ifindex
4ed8d95 TDLS: Tear down TDLS using wpas_drv_tlds_oper() if not external
831770b Cancel delayed scheduled scan when wpa_supplicant cleans up
69dd296 WDS: Fix WEP usage with nl80211 wds_sta=1
c8ebeda wpa_supplicant: Add support for VHT BSS membership selector
3f9a813 hostapd: Add a config option to control beaconing
182b2e5 Add missing host_to_le32() for big endian hosts
3f53c00 nl80211: Ignore disconnect event in case of locally generated request

Change-Id: Ia7368e71ae40966a92970ac82b002c09a7971d41
Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>
diff --git a/wpa_supplicant/ibss_rsn.c b/wpa_supplicant/ibss_rsn.c
index 687c042..62d68b8 100644
--- a/wpa_supplicant/ibss_rsn.c
+++ b/wpa_supplicant/ibss_rsn.c
@@ -15,6 +15,7 @@
 #include "ap/wpa_auth.h"
 #include "wpa_supplicant_i.h"
 #include "driver_i.h"
+#include "common/ieee802_11_defs.h"
 #include "ibss_rsn.h"
 
 
@@ -438,48 +439,135 @@
 }
 
 
-int ibss_rsn_start(struct ibss_rsn *ibss_rsn, const u8 *addr)
+static int ibss_rsn_send_auth(struct ibss_rsn *ibss_rsn, const u8 *da, int seq)
+{
+	struct ieee80211_mgmt auth;
+	const size_t auth_length = IEEE80211_HDRLEN + sizeof(auth.u.auth);
+	struct wpa_supplicant *wpa_s = ibss_rsn->wpa_s;
+
+	if (wpa_s->driver->send_frame == NULL)
+		return -1;
+
+	os_memset(&auth, 0, sizeof(auth));
+
+	auth.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+					  WLAN_FC_STYPE_AUTH);
+	os_memcpy(auth.da, da, ETH_ALEN);
+	os_memcpy(auth.sa, wpa_s->own_addr, ETH_ALEN);
+	os_memcpy(auth.bssid, wpa_s->bssid, ETH_ALEN);
+
+	auth.u.auth.auth_alg = host_to_le16(WLAN_AUTH_OPEN);
+	auth.u.auth.auth_transaction = host_to_le16(seq);
+	auth.u.auth.status_code = host_to_le16(WLAN_STATUS_SUCCESS);
+
+	wpa_printf(MSG_DEBUG, "RSN: IBSS TX Auth frame (SEQ %d) to " MACSTR,
+		   seq, MAC2STR(da));
+
+	return wpa_s->driver->send_frame(wpa_s->drv_priv, (u8 *) &auth,
+					 auth_length, 0);
+}
+
+
+static int ibss_rsn_is_auth_started(struct ibss_rsn_peer * peer)
+{
+	return peer->authentication_status &
+	       (IBSS_RSN_AUTH_BY_US | IBSS_RSN_AUTH_EAPOL_BY_US);
+}
+
+
+static struct ibss_rsn_peer *
+ibss_rsn_peer_init(struct ibss_rsn *ibss_rsn, const u8 *addr)
 {
 	struct ibss_rsn_peer *peer;
-
 	if (ibss_rsn == NULL)
-		return -1;
+		return NULL;
 
-	if (ibss_rsn_get_peer(ibss_rsn, addr)) {
-		wpa_printf(MSG_DEBUG, "RSN: IBSS Authenticator and Supplicant "
-			   "for peer " MACSTR " already running",
-			   MAC2STR(addr));
-		return 0;
+	peer = ibss_rsn_get_peer(ibss_rsn, addr);
+	if (peer) {
+		wpa_printf(MSG_DEBUG, "RSN: IBSS Supplicant for peer "MACSTR
+			   " already running", MAC2STR(addr));
+		return peer;
 	}
 
-	wpa_printf(MSG_DEBUG, "RSN: Starting IBSS Authenticator and "
-		   "Supplicant for peer " MACSTR, MAC2STR(addr));
+	wpa_printf(MSG_DEBUG, "RSN: Starting IBSS Supplicant for peer "MACSTR,
+		   MAC2STR(addr));
 
 	peer = os_zalloc(sizeof(*peer));
-	if (peer == NULL)
-		return -1;
+	if (peer == NULL) {
+		wpa_printf(MSG_DEBUG, "RSN: Could not allocate memory.");
+		return NULL;
+	}
 
 	peer->ibss_rsn = ibss_rsn;
 	os_memcpy(peer->addr, addr, ETH_ALEN);
+	peer->authentication_status = IBSS_RSN_AUTH_NOT_AUTHENTICATED;
 
-	if (ibss_rsn_supp_init(peer, ibss_rsn->wpa_s->own_addr, ibss_rsn->psk)
-	    < 0) {
+	if (ibss_rsn_supp_init(peer, ibss_rsn->wpa_s->own_addr,
+			       ibss_rsn->psk) < 0) {
 		ibss_rsn_free(peer);
-		return -1;
-	}
-
-	if (ibss_rsn_auth_init(ibss_rsn, peer) < 0) {
-		ibss_rsn_free(peer);
-		return -1;
+		return NULL;
 	}
 
 	peer->next = ibss_rsn->peers;
 	ibss_rsn->peers = peer;
 
+	return peer;
+}
+
+
+int ibss_rsn_start(struct ibss_rsn *ibss_rsn, const u8 *addr)
+{
+	struct ibss_rsn_peer *peer;
+	int res;
+
+	/* if the peer already exists, exit immediately */
+	peer = ibss_rsn_get_peer(ibss_rsn, addr);
+	if (peer)
+		return 0;
+
+	peer = ibss_rsn_peer_init(ibss_rsn, addr);
+	if (peer == NULL)
+		return -1;
+
+	/* Open Authentication: send first Authentication frame */
+	res = ibss_rsn_send_auth(ibss_rsn, addr, 1);
+	if (res) {
+		/*
+		 * The driver may not support Authentication frame exchange in
+		 * IBSS. Ignore authentication and go through EAPOL exchange.
+		 */
+		peer->authentication_status |= IBSS_RSN_AUTH_BY_US;
+		return ibss_rsn_auth_init(ibss_rsn, peer);
+	}
+
 	return 0;
 }
 
 
+static int ibss_rsn_peer_authenticated(struct ibss_rsn *ibss_rsn,
+				       struct ibss_rsn_peer *peer, int reason)
+{
+	int already_started;
+
+	if (ibss_rsn == NULL || peer == NULL)
+		return -1;
+
+	already_started = ibss_rsn_is_auth_started(peer);
+	peer->authentication_status |= reason;
+
+	if (already_started) {
+		wpa_printf(MSG_DEBUG, "RSN: IBSS Authenticator already "
+			   "started for peer " MACSTR, MAC2STR(peer->addr));
+		return 0;
+	}
+
+	wpa_printf(MSG_DEBUG, "RSN: Starting IBSS Authenticator "
+		   "for now-authenticated peer " MACSTR, MAC2STR(peer->addr));
+
+	return ibss_rsn_auth_init(ibss_rsn, peer);
+}
+
+
 void ibss_rsn_stop(struct ibss_rsn *ibss_rsn, const u8 *peermac)
 {
 	struct ibss_rsn_peer *peer, *prev;
@@ -617,10 +705,21 @@
 		return -1;
 	os_memcpy(tmp, buf, len);
 	if (supp) {
-		wpa_printf(MSG_DEBUG, "RSN: IBSS RX EAPOL for Supplicant");
+		peer->authentication_status |= IBSS_RSN_AUTH_EAPOL_BY_PEER;
+		wpa_printf(MSG_DEBUG, "RSN: IBSS RX EAPOL for Supplicant from "
+			   MACSTR, MAC2STR(peer->addr));
 		wpa_sm_rx_eapol(peer->supp, peer->addr, tmp, len);
 	} else {
-		wpa_printf(MSG_DEBUG, "RSN: IBSS RX EAPOL for Authenticator");
+		if (ibss_rsn_is_auth_started(peer) == 0) {
+			wpa_printf(MSG_DEBUG, "RSN: IBSS EAPOL for "
+				   "Authenticator dropped as " MACSTR " is not "
+				   "authenticated", MAC2STR(peer->addr));
+			os_free(tmp);
+			return -1;
+		}
+
+		wpa_printf(MSG_DEBUG, "RSN: IBSS RX EAPOL for Authenticator "
+			   "from "MACSTR, MAC2STR(peer->addr));
 		wpa_receive(ibss_rsn->auth_group, peer->auth, tmp, len);
 	}
 	os_free(tmp);
@@ -646,8 +745,16 @@
 		 * Create new IBSS peer based on an EAPOL message from the peer
 		 * Authenticator.
 		 */
-		if (ibss_rsn_start(ibss_rsn, src_addr) < 0)
+		peer = ibss_rsn_peer_init(ibss_rsn, src_addr);
+		if (peer == NULL)
 			return -1;
+
+		/* assume the peer is authenticated already */
+		wpa_printf(MSG_DEBUG, "RSN: IBSS Not using IBSS Auth for peer "
+			   MACSTR, MAC2STR(src_addr));
+		ibss_rsn_peer_authenticated(ibss_rsn, peer,
+					    IBSS_RSN_AUTH_EAPOL_BY_US);
+
 		return ibss_rsn_process_rx_eapol(ibss_rsn, ibss_rsn->peers,
 						 buf, len);
 	}
@@ -655,10 +762,89 @@
 	return 0;
 }
 
-
 void ibss_rsn_set_psk(struct ibss_rsn *ibss_rsn, const u8 *psk)
 {
 	if (ibss_rsn == NULL)
 		return;
 	os_memcpy(ibss_rsn->psk, psk, PMK_LEN);
 }
+
+
+static void ibss_rsn_handle_auth_1_of_2(struct ibss_rsn *ibss_rsn,
+					struct ibss_rsn_peer *peer,
+					const u8* addr)
+{
+	wpa_printf(MSG_DEBUG, "RSN: IBSS RX Auth frame (SEQ 1) from " MACSTR,
+		   MAC2STR(addr));
+
+	if (peer &&
+	    peer->authentication_status & IBSS_RSN_AUTH_EAPOL_BY_PEER) {
+		/*
+		 * A peer sent us an Authentication frame even though it already
+		 * started an EAPOL session. We should reinit state machines
+		 * here, but it's much more complicated than just deleting and
+		 * recreating the state machine
+		 */
+		wpa_printf(MSG_DEBUG, "RSN: IBSS Reinitializing station "
+			   MACSTR, MAC2STR(addr));
+
+		ibss_rsn_stop(ibss_rsn, addr);
+		peer = NULL;
+	}
+
+	if (!peer) {
+		peer = ibss_rsn_peer_init(ibss_rsn, addr);
+		if (!peer)
+			return;
+
+		wpa_printf(MSG_DEBUG, "RSN: IBSS Auth started by peer " MACSTR,
+			   MAC2STR(addr));
+	}
+
+	/* reply with an Authentication frame now, before sending an EAPOL */
+	ibss_rsn_send_auth(ibss_rsn, addr, 2);
+	/* no need to start another AUTH challenge in the other way.. */
+	ibss_rsn_peer_authenticated(ibss_rsn, peer, IBSS_RSN_AUTH_EAPOL_BY_US);
+}
+
+
+void ibss_rsn_handle_auth(struct ibss_rsn *ibss_rsn, const u8 *auth_frame,
+			  size_t len)
+{
+	const struct ieee80211_mgmt *header;
+	struct ibss_rsn_peer *peer;
+	size_t auth_length;
+
+	header = (const struct ieee80211_mgmt *) auth_frame;
+	auth_length = IEEE80211_HDRLEN + sizeof(header->u.auth);
+
+	if (ibss_rsn == NULL || len < auth_length)
+		return;
+
+	if (le_to_host16(header->u.auth.auth_alg) != WLAN_AUTH_OPEN ||
+	    le_to_host16(header->u.auth.status_code) != WLAN_STATUS_SUCCESS)
+		return;
+
+	peer = ibss_rsn_get_peer(ibss_rsn, header->sa);
+
+	switch (le_to_host16(header->u.auth.auth_transaction)) {
+	case 1:
+		ibss_rsn_handle_auth_1_of_2(ibss_rsn, peer, header->sa);
+		break;
+	case 2:
+		wpa_printf(MSG_DEBUG, "RSN: IBSS RX Auth frame (SEQ 2) from "
+			   MACSTR, MAC2STR(header->sa));
+		if (!peer) {
+			wpa_printf(MSG_DEBUG, "RSN: Received Auth seq 2 from "
+				   "unknown STA " MACSTR, MAC2STR(header->sa));
+			break;
+		}
+
+		/* authentication has been completed */
+		wpa_printf(MSG_DEBUG, "RSN: IBSS Auth completed with "MACSTR,
+			   MAC2STR(header->sa));
+		ibss_rsn_peer_authenticated(ibss_rsn, peer,
+					    IBSS_RSN_AUTH_BY_US);
+		break;
+	}
+}