Browse Source

SAE: Support external authentication offload for driver-SME cases

Extend the SME functionality to support the external authentication.
External authentication may be used by the drivers that do not define
separate commands for authentication and association
(~WPA_DRIVER_FLAGS_SME) but rely on wpa_supplicant's SME for the
authentication.

Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
Sunil Dutt 7 years ago
parent
commit
5ff39c1380

+ 10 - 0
wpa_supplicant/driver_i.h

@@ -1036,4 +1036,14 @@ static inline int wpa_drv_update_connect_params(
 						    mask);
 }
 
+static inline int
+wpa_drv_send_external_auth_status(struct wpa_supplicant *wpa_s,
+				  struct external_auth *params)
+{
+	if (!wpa_s->driver->send_external_auth_status)
+		return -1;
+	return wpa_s->driver->send_external_auth_status(wpa_s->drv_priv,
+							params);
+}
+
 #endif /* DRIVER_I_H */

+ 19 - 0
wpa_supplicant/events.c

@@ -4267,6 +4267,16 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
 				break;
 			}
 
+#ifdef CONFIG_SAE
+			if (stype == WLAN_FC_STYPE_AUTH &&
+			    !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) &&
+			    (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SAE)) {
+				sme_external_auth_mgmt_rx(
+					wpa_s, data->rx_mgmt.frame,
+					data->rx_mgmt.frame_len);
+				break;
+			}
+#endif /* CONFIG_SAE */
 			wpa_dbg(wpa_s, MSG_DEBUG, "AP: ignore received "
 				"management frame in non-AP mode");
 			break;
@@ -4579,6 +4589,15 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
 		wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_BEACON_LOSS);
 		bgscan_notify_beacon_loss(wpa_s);
 		break;
+	case EVENT_EXTERNAL_AUTH:
+#ifdef CONFIG_SAE
+		if (!wpa_s->current_ssid) {
+			wpa_printf(MSG_DEBUG, "SAE: current_ssid is NULL");
+			break;
+		}
+		sme_external_auth_trigger(wpa_s, data);
+#endif /* CONFIG_SAE */
+		break;
 	default:
 		wpa_msg(wpa_s, MSG_INFO, "Unknown event %d", event);
 		break;

+ 227 - 20
wpa_supplicant/sme.c

@@ -83,7 +83,7 @@ static int sme_set_sae_group(struct wpa_supplicant *wpa_s)
 
 static struct wpabuf * sme_auth_build_sae_commit(struct wpa_supplicant *wpa_s,
 						 struct wpa_ssid *ssid,
-						 const u8 *bssid)
+						 const u8 *bssid, int external)
 {
 	struct wpabuf *buf;
 	size_t len;
@@ -126,16 +126,18 @@ static struct wpabuf * sme_auth_build_sae_commit(struct wpa_supplicant *wpa_s,
 	buf = wpabuf_alloc(4 + SAE_COMMIT_MAX_LEN + len);
 	if (buf == NULL)
 		return NULL;
-
-	wpabuf_put_le16(buf, 1); /* Transaction seq# */
-	wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
+	if (!external) {
+		wpabuf_put_le16(buf, 1); /* Transaction seq# */
+		wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
+	}
 	sae_write_commit(&wpa_s->sme.sae, buf, wpa_s->sme.sae_token);
 
 	return buf;
 }
 
 
-static struct wpabuf * sme_auth_build_sae_confirm(struct wpa_supplicant *wpa_s)
+static struct wpabuf * sme_auth_build_sae_confirm(struct wpa_supplicant *wpa_s,
+						  int external)
 {
 	struct wpabuf *buf;
 
@@ -143,8 +145,10 @@ static struct wpabuf * sme_auth_build_sae_confirm(struct wpa_supplicant *wpa_s)
 	if (buf == NULL)
 		return NULL;
 
-	wpabuf_put_le16(buf, 2); /* Transaction seq# */
-	wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
+	if (!external) {
+		wpabuf_put_le16(buf, 2); /* Transaction seq# */
+		wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
+	}
 	sae_write_confirm(&wpa_s->sme.sae, buf);
 
 	return buf;
@@ -554,9 +558,9 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s,
 	if (!skip_auth && params.auth_alg == WPA_AUTH_ALG_SAE) {
 		if (start)
 			resp = sme_auth_build_sae_commit(wpa_s, ssid,
-							 bss->bssid);
+							 bss->bssid, 0);
 		else
-			resp = sme_auth_build_sae_confirm(wpa_s);
+			resp = sme_auth_build_sae_confirm(wpa_s, 0);
 		if (resp == NULL) {
 			wpas_connection_failed(wpa_s, bss->bssid);
 			return;
@@ -789,8 +793,150 @@ void sme_authenticate(struct wpa_supplicant *wpa_s,
 
 #ifdef CONFIG_SAE
 
+static int sme_external_auth_build_buf(struct wpabuf *buf,
+				       struct wpabuf *params,
+				       const u8 *sa, const u8 *da,
+				       u16 auth_transaction, u16 seq_num)
+{
+	struct ieee80211_mgmt *resp;
+
+	resp = wpabuf_put(buf, offsetof(struct ieee80211_mgmt,
+					u.auth.variable));
+
+	resp->frame_control = host_to_le16((WLAN_FC_TYPE_MGMT << 2) |
+					   (WLAN_FC_STYPE_AUTH << 4));
+	os_memcpy(resp->da, da, ETH_ALEN);
+	os_memcpy(resp->sa, sa, ETH_ALEN);
+	os_memcpy(resp->bssid, da, ETH_ALEN);
+	resp->u.auth.auth_alg = WLAN_AUTH_SAE;
+	resp->seq_ctrl = seq_num << 4;
+	resp->u.auth.auth_transaction = auth_transaction;
+	resp->u.auth.status_code = WLAN_STATUS_SUCCESS;
+	if (params)
+		wpabuf_put_buf(buf, params);
+
+	return 0;
+}
+
+
+static void sme_external_auth_send_sae_commit(struct wpa_supplicant *wpa_s,
+					      const u8 *bssid,
+					      struct wpa_ssid *ssid)
+{
+	struct wpabuf *resp, *buf;
+
+	resp = sme_auth_build_sae_commit(wpa_s, ssid, bssid, 1);
+	if (!resp)
+		return;
+
+	wpa_s->sme.sae.state = SAE_COMMITTED;
+	buf = wpabuf_alloc(4 + SAE_COMMIT_MAX_LEN + wpabuf_len(resp));
+	if (!buf) {
+		wpabuf_free(resp);
+		return;
+	}
+
+	wpa_s->sme.seq_num++;
+	sme_external_auth_build_buf(buf, resp, wpa_s->own_addr,
+				    bssid, 1, wpa_s->sme.seq_num);
+	wpa_drv_send_mlme(wpa_s, wpabuf_head(buf), wpabuf_len(buf), 1, 0);
+	wpabuf_free(resp);
+	wpabuf_free(buf);
+}
+
+
+static void sme_send_external_auth_status(struct wpa_supplicant *wpa_s,
+					  u16 status)
+{
+	struct external_auth params;
+
+	os_memset(&params, 0, sizeof(params));
+	params.status = status;
+	os_memcpy(params.ssid, wpa_s->sme.ext_auth.ssid,
+		  wpa_s->sme.ext_auth.ssid_len);
+	params.ssid_len = wpa_s->sme.ext_auth.ssid_len;
+	os_memcpy(params.bssid, wpa_s->sme.ext_auth.bssid, ETH_ALEN);
+	wpa_drv_send_external_auth_status(wpa_s, &params);
+}
+
+
+static void sme_handle_external_auth_start(struct wpa_supplicant *wpa_s,
+					   union wpa_event_data *data)
+{
+	struct wpa_ssid *ssid;
+	size_t ssid_str_len = data->external_auth.ssid_len;
+	u8 *ssid_str = data->external_auth.ssid;
+
+	/* Get the SSID conf from the ssid string obtained */
+	for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
+		if (!wpas_network_disabled(wpa_s, ssid) &&
+		    ssid_str_len == ssid->ssid_len &&
+		    os_memcmp(ssid_str, ssid->ssid, ssid_str_len) == 0)
+			break;
+	}
+	if (ssid)
+		sme_external_auth_send_sae_commit(wpa_s,
+						  data->external_auth.bssid,
+						  ssid);
+	else
+		sme_send_external_auth_status(wpa_s,
+					      WLAN_STATUS_UNSPECIFIED_FAILURE);
+}
+
+
+static void sme_external_auth_send_sae_confirm(struct wpa_supplicant *wpa_s,
+					       const u8 *da)
+{
+	struct wpabuf *resp, *buf;
+
+	resp = sme_auth_build_sae_confirm(wpa_s, 1);
+	if (!resp) {
+		wpa_printf(MSG_DEBUG, "SAE: Confirm message buf alloc failure");
+		return;
+	}
+
+	wpa_s->sme.sae.state = SAE_CONFIRMED;
+	buf = wpabuf_alloc(4 + SAE_CONFIRM_MAX_LEN + wpabuf_len(resp));
+	if (!buf) {
+		wpa_printf(MSG_DEBUG, "SAE: Auth Confirm buf alloc failure");
+		wpabuf_free(resp);
+		return;
+	}
+	wpa_s->sme.seq_num++;
+	sme_external_auth_build_buf(buf, resp, wpa_s->own_addr,
+				    da, 2, wpa_s->sme.seq_num);
+	wpa_drv_send_mlme(wpa_s, wpabuf_head(buf), wpabuf_len(buf), 1, 0);
+	wpabuf_free(resp);
+	wpabuf_free(buf);
+}
+
+
+void sme_external_auth_trigger(struct wpa_supplicant *wpa_s,
+			       union wpa_event_data *data)
+{
+	if (RSN_SELECTOR_GET(&data->external_auth.key_mgmt_suite) !=
+	    RSN_AUTH_KEY_MGMT_SAE)
+		return;
+
+	if (data->external_auth.action == EXT_AUTH_START) {
+		os_memcpy(&wpa_s->sme.ext_auth, data,
+			  sizeof(struct external_auth));
+		wpa_s->sme.seq_num = 0;
+		wpa_s->sme.sae.state = SAE_NOTHING;
+		wpa_s->sme.sae.send_confirm = 0;
+		wpa_s->sme.sae_group_index = 0;
+		sme_handle_external_auth_start(wpa_s, data);
+	} else if (data->external_auth.action == EXT_AUTH_ABORT) {
+		/* Report failure to driver for the wrong trigger */
+		sme_send_external_auth_status(wpa_s,
+					      WLAN_STATUS_UNSPECIFIED_FAILURE);
+	}
+}
+
+
 static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction,
-			u16 status_code, const u8 *data, size_t len)
+			u16 status_code, const u8 *data, size_t len,
+			int external, const u8 *sa)
 {
 	int *groups;
 
@@ -800,7 +946,7 @@ static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction,
 	if (auth_transaction == 1 &&
 	    status_code == WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ &&
 	    wpa_s->sme.sae.state == SAE_COMMITTED &&
-	    wpa_s->current_bss && wpa_s->current_ssid) {
+	    (external || wpa_s->current_bss) && wpa_s->current_ssid) {
 		int default_groups[] = { 19, 20, 21, 25, 26, 0 };
 		u16 group;
 
@@ -827,22 +973,32 @@ static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction,
 		wpabuf_free(wpa_s->sme.sae_token);
 		wpa_s->sme.sae_token = wpabuf_alloc_copy(data + sizeof(le16),
 							 len - sizeof(le16));
-		sme_send_authentication(wpa_s, wpa_s->current_bss,
-					wpa_s->current_ssid, 1);
+		if (!external)
+			sme_send_authentication(wpa_s, wpa_s->current_bss,
+						wpa_s->current_ssid, 1);
+		else
+			sme_external_auth_send_sae_commit(
+				wpa_s, wpa_s->sme.ext_auth.bssid,
+				wpa_s->current_ssid);
 		return 0;
 	}
 
 	if (auth_transaction == 1 &&
 	    status_code == WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED &&
 	    wpa_s->sme.sae.state == SAE_COMMITTED &&
-	    wpa_s->current_bss && wpa_s->current_ssid) {
+	    (external || wpa_s->current_bss) && wpa_s->current_ssid) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "SME: SAE group not supported");
 		wpa_s->sme.sae_group_index++;
 		if (sme_set_sae_group(wpa_s) < 0)
 			return -1; /* no other groups enabled */
 		wpa_dbg(wpa_s, MSG_DEBUG, "SME: Try next enabled SAE group");
-		sme_send_authentication(wpa_s, wpa_s->current_bss,
-					wpa_s->current_ssid, 1);
+		if (!external)
+			sme_send_authentication(wpa_s, wpa_s->current_bss,
+						wpa_s->current_ssid, 1);
+		else
+			sme_external_auth_send_sae_commit(
+				wpa_s, wpa_s->sme.ext_auth.bssid,
+				wpa_s->current_ssid);
 		return 0;
 	}
 
@@ -855,7 +1011,7 @@ static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction,
 		groups = wpa_s->conf->sae_groups;
 
 		wpa_dbg(wpa_s, MSG_DEBUG, "SME SAE commit");
-		if (wpa_s->current_bss == NULL ||
+		if ((!external && wpa_s->current_bss == NULL) ||
 		    wpa_s->current_ssid == NULL)
 			return -1;
 		if (wpa_s->sme.sae.state != SAE_COMMITTED)
@@ -880,8 +1036,11 @@ static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction,
 
 		wpabuf_free(wpa_s->sme.sae_token);
 		wpa_s->sme.sae_token = NULL;
-		sme_send_authentication(wpa_s, wpa_s->current_bss,
-					wpa_s->current_ssid, 0);
+		if (!external)
+			sme_send_authentication(wpa_s, wpa_s->current_bss,
+						wpa_s->current_ssid, 0);
+		else
+			sme_external_auth_send_sae_confirm(wpa_s, sa);
 		return 0;
 	} else if (auth_transaction == 2) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "SME SAE confirm");
@@ -891,11 +1050,59 @@ static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction,
 			return -1;
 		wpa_s->sme.sae.state = SAE_ACCEPTED;
 		sae_clear_temp_data(&wpa_s->sme.sae);
+
+		if (external) {
+			/* Report success to driver */
+			sme_send_external_auth_status(wpa_s,
+						      WLAN_STATUS_SUCCESS);
+		}
+
 		return 1;
 	}
 
 	return -1;
 }
+
+
+void sme_external_auth_mgmt_rx(struct wpa_supplicant *wpa_s,
+			       const u8 *auth_frame, size_t len)
+{
+	const struct ieee80211_mgmt *header;
+	size_t auth_length;
+
+	header = (const struct ieee80211_mgmt *) auth_frame;
+	auth_length = IEEE80211_HDRLEN + sizeof(header->u.auth);
+
+	if (len < auth_length) {
+		/* Notify failure to the driver */
+		sme_send_external_auth_status(wpa_s,
+					      WLAN_STATUS_UNSPECIFIED_FAILURE);
+		return;
+	}
+
+	if (header->u.auth.auth_alg == WLAN_AUTH_SAE) {
+		int res;
+
+		res = sme_sae_auth(wpa_s, header->u.auth.auth_transaction,
+				   header->u.auth.status_code,
+				   header->u.auth.variable,
+				   len - auth_length, 1, header->sa);
+		if (res < 0) {
+			/* Notify failure to the driver */
+			sme_send_external_auth_status(
+				wpa_s, WLAN_STATUS_UNSPECIFIED_FAILURE);
+			return;
+		}
+		if (res != 1)
+			return;
+
+		wpa_printf(MSG_DEBUG,
+			   "SME: SAE completed - setting PMK for 4-way handshake");
+		wpa_sm_set_pmk(wpa_s->wpa, wpa_s->sme.sae.pmk, PMK_LEN,
+			       wpa_s->sme.sae.pmkid, wpa_s->pending_bssid);
+	}
+}
+
 #endif /* CONFIG_SAE */
 
 
@@ -936,7 +1143,7 @@ void sme_event_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data)
 		int res;
 		res = sme_sae_auth(wpa_s, data->auth.auth_transaction,
 				   data->auth.status_code, data->auth.ies,
-				   data->auth.ies_len);
+				   data->auth.ies_len, 0, NULL);
 		if (res < 0) {
 			wpas_connection_failed(wpa_s, wpa_s->pending_bssid);
 			wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);

+ 14 - 0
wpa_supplicant/sme.h

@@ -38,6 +38,10 @@ void sme_deinit(struct wpa_supplicant *wpa_s);
 
 int sme_proc_obss_scan(struct wpa_supplicant *wpa_s);
 void sme_sched_obss_scan(struct wpa_supplicant *wpa_s, int enable);
+void sme_external_auth_trigger(struct wpa_supplicant *wpa_s,
+			       union wpa_event_data *data);
+void sme_external_auth_mgmt_rx(struct wpa_supplicant *wpa_s,
+			       const u8 *auth_frame, size_t len);
 
 #else /* CONFIG_SME */
 
@@ -113,6 +117,16 @@ static inline void sme_sched_obss_scan(struct wpa_supplicant *wpa_s,
 {
 }
 
+static inline void sme_external_auth_trigger(struct wpa_supplicant *wpa_s,
+					     union wpa_event_data *data)
+{
+}
+
+static inline void sme_external_auth_mgmt_rx(struct wpa_supplicant *wpa_s,
+					     const u8 *auth_frame, size_t len)
+{
+}
+
 #endif /* CONFIG_SME */
 
 #endif /* SME_H */

+ 4 - 0
wpa_supplicant/wpa_supplicant.c

@@ -2473,6 +2473,10 @@ static u8 * wpas_populate_assoc_ies(
 	}
 #endif /* CONFIG_FILS */
 #endif /* IEEE8021X_EAPOL */
+#ifdef CONFIG_SAE
+	if (wpa_s->key_mgmt & (WPA_KEY_MGMT_SAE | WPA_KEY_MGMT_FT_SAE))
+		algs = WPA_AUTH_ALG_SAE;
+#endif /* CONFIG_SAE */
 
 	wpa_dbg(wpa_s, MSG_DEBUG, "Automatic auth_alg selection: 0x%x", algs);
 	if (ssid->auth_alg) {

+ 2 - 0
wpa_supplicant/wpa_supplicant_i.h

@@ -789,6 +789,8 @@ struct wpa_supplicant {
 		struct wpabuf *sae_token;
 		int sae_group_index;
 		unsigned int sae_pmksa_caching:1;
+		u16 seq_num;
+		struct external_auth ext_auth;
 #endif /* CONFIG_SAE */
 	} sme;
 #endif /* CONFIG_SME */