Patchwork [WIP,2/2] Support for handling Auth frames in IBSS mode.

login
register
mail settings
Submitter Nicolas Cavallari
Date April 8, 2013, 9:55 a.m.
Message ID <1365414953-25734-3-git-send-email-cavallar@lri.fr>
Download mbox | patch
Permalink /patch/234720/
State Superseded
Headers show

Comments

Nicolas Cavallari - April 8, 2013, 9:55 a.m.
Currently, the kernel exchanges Open System Auth frames
and wpa_supplicant exchanges EAPOL frames at the same time,
in violation of the 802.11 standard, which specifies that
Open System Authentication must optionally take place
before starting the Four Way Handshake, but not at the
same time.

The kernel takes further steps and will reset a station entry
when it sends an 1/2 open system authentication frame.
Doing this after wpa_supplicant have already exchanged frames
will break the Four Way Handshake, as the other party
is uninformed about our reset.

The solution is to make wpa_supplicant handle auth frames,
and not send EAPOL until either Open System Authentication
has completed, or until wpa_supplicant decides that
it will not do Open System Authentication.

This patch will make wpasupplicant register for auth frames,
handle them using the existing AP event code and will
defer the initialization of the authenticator until we
authenticated the remote station with Open System Authentication
or the remote station sent a Open System Authentication or a EAPOL
to us.

Signed-hostap: Nicolas Cavallari <cavallar@lri.fr>
---
 src/drivers/driver_nl80211.c |    9 ++
 wpa_supplicant/events.c      |   26 +++++-
 wpa_supplicant/ibss_rsn.c    |  197 +++++++++++++++++++++++++++++++++++++-----
 wpa_supplicant/ibss_rsn.h    |   14 +++
 4 files changed, 224 insertions(+), 22 deletions(-)

Patch

diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index a932b8d..fbc8d32 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -309,6 +309,9 @@  static int nl80211_send_frame_cmd(struct i802_bss *bss,
 				  unsigned int freq, unsigned int wait,
 				  const u8 *buf, size_t buf_len, u64 *cookie,
 				  int no_cck, int no_ack, int offchanok);
+static int nl80211_register_frame(struct i802_bss *bss,
+				  struct nl_handle *hl_handle,
+				  u16 type, const u8 *match, size_t match_len);
 static int wpa_driver_nl80211_probe_req_report(struct i802_bss *bss,
 					       int report);
 #ifdef ANDROID
@@ -1579,6 +1582,12 @@  static void mlme_event_join_ibss(struct wpa_driver_nl80211_data *drv,
 		return;
 	}
 	os_memcpy(drv->bssid, nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN);
+
+	nl80211_register_frame(&drv->first_bss, drv->first_bss.nl_mgmt,
+			       ((WLAN_FC_TYPE_MGMT << 2) |
+				(WLAN_FC_STYPE_AUTH << 4)),
+			       (u8*)"\0\0", 2);
+
 	drv->associated = 1;
 	wpa_printf(MSG_DEBUG, "nl80211: IBSS " MACSTR " joined",
 		   MAC2STR(drv->bssid));
diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
index 8d276c5..cee1bf6 100644
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -2271,6 +2271,18 @@  static void wpa_supplicant_event_ibss_rsn_start(struct wpa_supplicant *wpa_s,
 
 	ibss_rsn_start(wpa_s->ibss_rsn, data->ibss_rsn_start.peer);
 }
+
+static void wpa_supplicant_event_ibss_auth(struct wpa_supplicant *wpa_s,
+					   union wpa_event_data *data)
+{
+	struct wpa_ssid *ssid = wpa_s->current_ssid;
+	if (ssid == NULL)
+		return;
+	if (ssid->mode != WPAS_MODE_IBSS || !wpa_key_mgmt_wpa(ssid->key_mgmt))
+		return;
+	ibss_rsn_handle_auth(wpa_s->ibss_rsn, data->rx_mgmt.frame,
+			     data->rx_mgmt.frame_len);
+}
 #endif /* CONFIG_IBSS_RSN */
 
 
@@ -2664,6 +2676,8 @@  void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
 				  data->ch_switch.ch_offset);
 #endif /* CONFIG_AP */
 		break;
+#endif /* CONFIG_AP */
+#if defined(CONFIG_AP) || defined(CONFIG_IBSS_RSN)
 	case EVENT_RX_MGMT: {
 		u16 fc, stype;
 		const struct ieee80211_mgmt *mgmt;
@@ -2673,7 +2687,9 @@  void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
 		fc = le_to_host16(mgmt->frame_control);
 		stype = WLAN_FC_GET_STYPE(fc);
 
+#ifdef CONFIG_AP
 		if (wpa_s->ap_iface == NULL) {
+#endif /* CONFIG_AP */
 #ifdef CONFIG_P2P
 			if (stype == WLAN_FC_STYPE_PROBE_REQ &&
 			    data->rx_mgmt.frame_len > 24) {
@@ -2689,9 +2705,16 @@  void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
 				break;
 			}
 #endif /* CONFIG_P2P */
+#ifdef CONFIG_IBSS_RSN
+			if (stype == WLAN_FC_STYPE_AUTH &&
+			    data->rx_mgmt.frame_len > 24)
+				wpa_supplicant_event_ibss_auth(wpa_s, data);
+			else
+#endif /* CONFIG_IBSS_RSN */
 			wpa_dbg(wpa_s, MSG_DEBUG, "AP: ignore received "
 				"management frame in non-AP mode");
 			break;
+#ifdef CONFIG_AP
 		}
 
 		if (stype == WLAN_FC_STYPE_PROBE_REQ &&
@@ -2707,9 +2730,10 @@  void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
 		}
 
 		ap_mgmt_rx(wpa_s, &data->rx_mgmt);
+#endif /* CONFIG_AP */
 		break;
 		}
-#endif /* CONFIG_AP */
+#endif /* CONFIG_AP || CONFIG_IBSS_RSN */
 	case EVENT_RX_ACTION:
 		wpa_dbg(wpa_s, MSG_DEBUG, "Received Action frame: SA=" MACSTR
 			" Category=%u DataLen=%d freq=%d MHz",
diff --git a/wpa_supplicant/ibss_rsn.c b/wpa_supplicant/ibss_rsn.c
index 046f181..4b5ccd9 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"
 
 
@@ -429,48 +430,118 @@  static int ibss_rsn_auth_init(struct ibss_rsn *ibss_rsn,
 	return 0;
 }
 
+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;
 
-int ibss_rsn_start(struct ibss_rsn *ibss_rsn, const u8 *addr)
+	auth.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+					  WLAN_FC_STYPE_AUTH);
+	auth.duration = 0; // filled by kernel.
+	memcpy(auth.da, da, 6);
+	memcpy(auth.sa, wpa_s->own_addr, 6);
+	memcpy(auth.bssid, wpa_s->bssid, 6);
+	auth.seq_ctrl = 0; // filled by kernel.
+	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);
+
+	if (wpa_s->driver->send_frame == NULL)
+		return -1;
+	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)
 {
-	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, int *was_created)
+{
+	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));
+		if (was_created)
+			*was_created = 0;
+		return peer;
 	}
 
-	wpa_printf(MSG_DEBUG, "RSN: Starting IBSS Authenticator and "
-		   "Supplicant for peer " MACSTR, MAC2STR(addr));
+	// Start the supplicant now so we are ready to accept any EAPOL from
+	// peers that don't use Open System Authentication.
+	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) {
 		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;
+	if (was_created)
+		*was_created = 1;
+
+	return peer;
+}
 
+
+int ibss_rsn_start(struct ibss_rsn *ibss_rsn, const u8 *addr)
+{
+	int was_created = 0;
+	struct ibss_rsn_peer *p;
+	p = ibss_rsn_peer_init(ibss_rsn, addr, &was_created);
+	if (p == NULL)
+		return -1;
+	// We use open system authentication, do not start authenticator until
+	// it completes or the other side acknowledge us.
+	if (was_created)
+		ibss_rsn_send_auth(ibss_rsn, addr, 1);
 	return 0;
 }
 
+static int ibss_rsn_peer_authenticated(struct ibss_rsn *ibss_rsn,
+				       struct ibss_rsn_peer * peer,
+				       int reason)
+{
+	if (ibss_rsn == NULL || peer == NULL)
+		return -1;
+	int 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)
 {
@@ -600,6 +671,7 @@  static int ibss_rsn_process_rx_eapol(struct ibss_rsn *ibss_rsn,
 	int supp;
 	u8 *tmp;
 
+
 	supp = ibss_rsn_eapol_dst_supp(buf, len);
 	if (supp < 0)
 		return -1;
@@ -609,10 +681,19 @@  static int ibss_rsn_process_rx_eapol(struct ibss_rsn *ibss_rsn,
 		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));
+			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);
@@ -638,8 +719,15 @@  int ibss_rsn_rx_eapol(struct ibss_rsn *ibss_rsn, const u8 *src_addr,
 		 * 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, NULL);
+		if (peer == NULL)
 			return -1;
+		// Don't use open system authentication,
+		// just do it implicitly
+		wpa_printf(MSG_DEBUG, "RSN: 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);
 	}
@@ -654,3 +742,70 @@  void ibss_rsn_set_psk(struct ibss_rsn *ibss_rsn, const u8 *psk)
 		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 Auth frame (SEQ 1) from "MACSTR,
+		   MAC2STR(addr));
+	if (peer != NULL && peer->authentication_status &
+	    IBSS_RSN_AUTH_EAPOL_BY_PEER) {
+		// We should reinit state machines here, but it's much
+		// more complicated than just deleting and recreating
+		// all the state machines.
+		wpa_printf(MSG_DEBUG, "RSN: IBSS: Reinitializing station.");
+		ibss_rsn_stop(ibss_rsn, addr);
+		peer = NULL;
+	}
+	if (peer == NULL) {
+		peer = ibss_rsn_peer_init(ibss_rsn, addr, NULL);
+		if (peer == NULL)
+			return;
+		wpa_printf(MSG_DEBUG, "RSN: IBSS Auth started by peer.");
+
+		// send it now, before sending an EAPOL.
+		ibss_rsn_send_auth(ibss_rsn, addr, 2);
+
+		// No need to do open system if the other does.
+		ibss_rsn_peer_authenticated(ibss_rsn, peer,
+					    IBSS_RSN_AUTH_EAPOL_BY_US);
+	} else
+		ibss_rsn_send_auth(ibss_rsn, addr, 2);
+}
+
+
+void ibss_rsn_handle_auth(struct ibss_rsn *ibss_rsn, const u8 *auth_frame,
+			  size_t len) {
+	const struct ieee80211_mgmt* header
+		= (const struct ieee80211_mgmt*)auth_frame;
+	const size_t auth_length = IEEE80211_HDRLEN + sizeof header->u.auth;
+	struct ibss_rsn_peer *peer;
+	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);
+	// peer can be NULL here
+
+	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 Auth frame (SEQ 2) from "
+			   MACSTR, MAC2STR(header->sa));
+		if (peer == NULL) {
+			wpa_printf(MSG_DEBUG, "RSN: Received Auth seq 2 from "
+				   "unknown sta "MACSTR, MAC2STR(header->sa));
+			break;
+		}
+		// authentication completed, start negotiating.
+		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);
+	}
+}
diff --git a/wpa_supplicant/ibss_rsn.h b/wpa_supplicant/ibss_rsn.h
index 1da94ab..923b9df 100644
--- a/wpa_supplicant/ibss_rsn.h
+++ b/wpa_supplicant/ibss_rsn.h
@@ -11,6 +11,17 @@ 
 
 struct ibss_rsn;
 
+// no authentication
+#define IBSS_RSN_AUTH_NOT_AUTHENTICATED 0
+// remote sent an eapol
+#define IBSS_RSN_AUTH_EAPOL_BY_PEER 1
+
+// We did open system authentication.
+#define IBSS_RSN_AUTH_BY_US 2
+// We sent an EAPOL
+#define IBSS_RSN_AUTH_EAPOL_BY_US 4
+
+
 struct ibss_rsn_peer {
 	struct ibss_rsn_peer *next;
 	struct ibss_rsn *ibss_rsn;
@@ -23,6 +34,7 @@  struct ibss_rsn_peer {
 	size_t supp_ie_len;
 
 	struct wpa_state_machine *auth;
+	int authentication_status;
 };
 
 struct ibss_rsn {
@@ -40,5 +52,7 @@  void ibss_rsn_stop(struct ibss_rsn *ibss_rsn, const u8 *peermac);
 int ibss_rsn_rx_eapol(struct ibss_rsn *ibss_rsn, const u8 *src_addr,
 		      const u8 *buf, size_t len);
 void ibss_rsn_set_psk(struct ibss_rsn *ibss_rsn, const u8 *psk);
+void ibss_rsn_handle_auth(struct ibss_rsn *ibss_rsn, const u8 *auth_frame,
+			  size_t len);
 
 #endif /* IBSS_RSN_H */