[PATCH 5/8] STA: Support Extended Key ID

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Support Extended Key ID in wpa_supplicant according to
IEEE 802.11-2016 for infrastructure (AP) associations.

Extended Key ID allows to rekey pairwise keys without the otherwise
unavoidable MPDU losses on a busy link. The standard is fully backward
compatible, allowing STAs to also connect to APs not supporting it.

The standard is not covering how to use it with extensions like FILS or
FT and wpa_supplicant sticks to the save settings by default but allows
the user to enable non-standard Extended Key ID support for FT and FILS.

BASIC Extended Key ID support does nothing not clearly covered by the
standard and force it to be off for compatibility when needed.
FT0, FILS0 and FILS_CUSTOM are not standardized extensions to also allow
Extended Key ID support to be used with FT and/or FILS.

FT0, FILS0 and FILS_CUSTOM are not standardized extensions allowing to
use Extended Key ID also with FT and/or FILS.
FILS0 and FILS_CUSTOM are both detecting when the AP is using the other
mode and switch over to it.

Signed-off-by: Alexander Wetzel <alexander@xxxxxxxxxxxxxx>
---
 src/rsn_supp/wpa.c                      | 183 ++++++++++++++++++++++--
 src/rsn_supp/wpa.h                      |  14 ++
 src/rsn_supp/wpa_ft.c                   |  27 +++-
 src/rsn_supp/wpa_i.h                    |   3 +
 src/rsn_supp/wpa_ie.c                   |   7 +
 wpa_supplicant/ap.c                     |   1 +
 wpa_supplicant/config.c                 |  50 +++++++
 wpa_supplicant/config_file.c            |   1 +
 wpa_supplicant/config_ssid.h            |   8 ++
 wpa_supplicant/ctrl_iface.c             |   3 +
 wpa_supplicant/dbus/dbus_new_handlers.c |   3 +-
 wpa_supplicant/driver_i.h               |   9 +-
 wpa_supplicant/wpa_cli.c                |   2 +-
 wpa_supplicant/wpa_supplicant.c         |  24 +++-
 wpa_supplicant/wpa_supplicant.conf      |  38 +++++
 wpa_supplicant/wpas_glue.c              |   7 +-
 16 files changed, 359 insertions(+), 21 deletions(-)

diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c
index 5bb47bcbe..dddaff47a 100644
--- a/src/rsn_supp/wpa.c
+++ b/src/rsn_supp/wpa.c
@@ -184,6 +184,7 @@ void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise)
 	u8 bssid[ETH_ALEN], *rbuf, *key_mic, *mic;
 
 	if (pairwise && sm->wpa_deny_ptk0_rekey &&
+	    !sm->use_extended_key_id &&
 	    wpa_sm_get_state(sm) == WPA_COMPLETED) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
 			"WPA: PTK0 rekey not allowed, reconnecting");
@@ -607,6 +608,94 @@ static int wpa_derive_ptk(struct wpa_sm *sm, const unsigned char *src_addr,
 			      sm->pairwise_cipher, z, z_len);
 }
 
+#ifdef CONFIG_FILS
+static int fils_handle_extended_key_id(struct wpa_sm *sm,
+				       struct wpa_eapol_ie_parse *kde)
+{
+	struct wpa_ie_data rsn;
+
+	if (sm->extended_key_id & EXT_KEY_ID_FILS &&
+	    sm->pairwise_cipher != WPA_CIPHER_TKIP &&
+	    !wpa_parse_wpa_ie_rsn(kde->rsn_ie, kde->rsn_ie_len, &rsn) &&
+	    rsn.capabilities & WPA_CAPABILITY_EXT_KEY_ID_FOR_UNICAST) {
+		sm->use_extended_key_id = 1;
+
+		wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+			"FILS: using Extended Key ID (%s)",
+			sm->extended_key_id & EXT_KEY_ID_FILS0 ? "FILS0" :
+								 "FILS_CUSTOM");
+		if (kde->key_id && sm->extended_key_id & EXT_KEY_ID_FILS0)
+			wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+				"KeyID in FILS0, using FILS_CUSTOM instead");
+		else if (!kde->key_id &&
+			   sm->extended_key_id & EXT_KEY_ID_FILS_CUSTOM)
+			wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+				"No KeyID in FILS_CUSTOM, using FILS0 instead");
+		if (kde->key_id) {
+			if (kde->key_id[0] & 0xfe) {
+				wpa_msg(sm->ctx->msg_ctx, MSG_ERROR,
+					"FILS: Invalid KeyID: %d",
+					kde->key_id[0]);
+				return -1;
+			}
+			sm->keyidx_active = kde->key_id[0];
+		} else {
+			sm->keyidx_active = 0;
+		}
+	} else {
+		sm->use_extended_key_id = 0;
+		sm->keyidx_active = 0;
+	}
+	return 0;
+}
+#endif /* CONFIG_FILS */
+
+static int handle_extended_key_id(struct wpa_sm *sm,
+				  struct wpa_eapol_ie_parse *kde)
+{
+	struct wpa_ie_data rsn;
+
+	/* IEEE 802.11-2016 requires the Extended Key ID bit to be set
+	 * in the RSN capabilities for both STAs to use it with CCMP/GCMP.
+	 * How to handle that in combination with FT/FILS is not specified and
+	 * therefore depends on configuration settings.
+	 */
+	if (sm->extended_key_id && sm->pairwise_cipher != WPA_CIPHER_TKIP &&
+	    (!(wpa_key_mgmt_ft(sm->key_mgmt) ||
+	       wpa_key_mgmt_fils(sm->key_mgmt)) ||
+	     (wpa_key_mgmt_ft(sm->key_mgmt) &&
+	      sm->extended_key_id & EXT_KEY_ID_FT0) ||
+	     (wpa_key_mgmt_fils(sm->key_mgmt) &&
+	      sm->extended_key_id & EXT_KEY_ID_FILS)) &&
+	    !wpa_parse_wpa_ie_rsn(kde->rsn_ie, kde->rsn_ie_len, &rsn) &&
+	    rsn.capabilities & WPA_CAPABILITY_EXT_KEY_ID_FOR_UNICAST) {
+		if (!kde->key_id) {
+			wpa_msg(sm->ctx->msg_ctx, MSG_ERROR,
+				"WPA: No KeyID in Extended Key ID handshake");
+			return -1;
+		} else if (kde->key_id[0] & 0xfe) {
+			wpa_msg(sm->ctx->msg_ctx, MSG_ERROR,
+				"WPA: Invalid KeyID: %d", kde->key_id[0]);
+			return -1;
+		}
+		wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+			"WPA: Using Extended Key ID");
+		sm->keyidx_active = kde->key_id[0];
+		sm->use_extended_key_id = 1;
+	} else {
+		if (kde->key_id && kde->key_id[0]) {
+			wpa_msg(sm->ctx->msg_ctx, MSG_ERROR,
+				"Non-zero Extended Key ID KeyID in PTK0 handshake");
+			return -1;
+		} else if (kde->key_id) {
+			wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+				"Extended Key ID KeyID in PTK0 handshake");
+		}
+		sm->keyidx_active = 0;
+		sm->use_extended_key_id = 0;
+	}
+	return 0;
+}
 
 static void wpa_supplicant_process_1_of_4(struct wpa_sm *sm,
 					  const unsigned char *src_addr,
@@ -626,7 +715,8 @@ static void wpa_supplicant_process_1_of_4(struct wpa_sm *sm,
 		return;
 	}
 
-	if (sm->wpa_deny_ptk0_rekey && wpa_sm_get_state(sm) == WPA_COMPLETED) {
+	if (sm->wpa_deny_ptk0_rekey && !sm->use_extended_key_id &&
+	    wpa_sm_get_state(sm) == WPA_COMPLETED) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
 			"WPA: PTK0 rekey not allowed, reconnecting");
 		wpa_sm_reconnect(sm);
@@ -762,9 +852,10 @@ static void wpa_supplicant_key_neg_complete(struct wpa_sm *sm,
 {
 	wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
 		"WPA: Key negotiation completed with "
-		MACSTR " [PTK=%s GTK=%s]", MAC2STR(addr),
+		MACSTR " [PTK=%s GTK=%s%s]", MAC2STR(addr),
 		wpa_cipher_txt(sm->pairwise_cipher),
-		wpa_cipher_txt(sm->group_cipher));
+		wpa_cipher_txt(sm->group_cipher),
+		sm->use_extended_key_id ? " Extended_Key_ID" : "");
 	wpa_sm_cancel_auth_timeout(sm);
 	wpa_sm_set_state(sm, WPA_COMPLETED);
 
@@ -859,13 +950,15 @@ static int wpa_supplicant_install_ptk(struct wpa_sm *sm,
 		wpa_hexdump(MSG_DEBUG, "WPA: RSC", key_rsc, rsclen);
 	}
 
-	if (wpa_sm_set_key(sm, alg, sm->bssid, 0, 1, key_rsc, rsclen,
-			   sm->ptk.tk, keylen,
+	if (wpa_sm_set_key(sm, alg, sm->bssid, sm->keyidx_active, 1, key_rsc,
+			   rsclen, sm->ptk.tk, keylen,
 			   KEY_FLAG_PAIRWISE | key_flag) < 0) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
-			"WPA: Failed to set PTK to the "
-			"driver (alg=%d keylen=%d bssid=" MACSTR ")",
-			alg, keylen, MAC2STR(sm->bssid));
+			"WPA: Failed to set PTK to the driver"
+			"(alg=%d keylen=%d bssid=" MACSTR
+			" idx=%d use_extended_key_id=%d key_flag=0x%x)",
+			alg, keylen, MAC2STR(sm->bssid),
+			sm->keyidx_active, sm->use_extended_key_id, key_flag);
 		return -1;
 	}
 
@@ -879,7 +972,22 @@ static int wpa_supplicant_install_ptk(struct wpa_sm *sm,
 		eloop_register_timeout(sm->wpa_ptk_rekey, 0, wpa_sm_rekey_ptk,
 				       sm, NULL);
 	}
+	return 0;
+}
 
+static int wpa_supplicant_activate_ptk(struct wpa_sm *sm)
+{
+	wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+		"WPA: Activate PTK (idx=%d bssid=" MACSTR ")",
+		sm->keyidx_active, MAC2STR(sm->bssid));
+
+	if (wpa_sm_set_key(sm, 0, sm->bssid, sm->keyidx_active,
+	    0, NULL, 0, NULL, 0,  KEY_FLAG_PAIRWISE_RX_TX_MODIFY) < 0) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_ERROR,
+			"WPA: Failed to activate PTK for Tx (idx=%d bssid="
+			MACSTR ")", sm->keyidx_active, MAC2STR(sm->bssid));
+		return -1;
+	}
 	return 0;
 }
 
@@ -1582,6 +1690,9 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm,
 	if (wpa_supplicant_validate_ie(sm, sm->bssid, &ie) < 0)
 		goto failed;
 
+	if (handle_extended_key_id(sm, &ie))
+		goto failed;
+
 	if (os_memcmp(sm->anonce, key->key_nonce, WPA_NONCE_LEN) != 0) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
 			"WPA: ANonce from message 1 of 4-Way Handshake "
@@ -1626,6 +1737,10 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm,
 		}
 	}
 #endif /* CONFIG_OCV */
+	if (sm->use_extended_key_id) {
+		if (wpa_supplicant_install_ptk(sm, key, KEY_FLAG_RX))
+			goto failed;
+	}
 
 	if (wpa_supplicant_send_4_of_4(sm, sm->bssid, key, ver, key_info,
 				       &sm->ptk) < 0) {
@@ -1638,8 +1753,13 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm,
 	sm->renew_snonce = 1;
 
 	if (key_info & WPA_KEY_INFO_INSTALL) {
-		if (wpa_supplicant_install_ptk(sm, key, KEY_FLAG_RX_TX))
+		if (sm->use_extended_key_id) {
+			if (wpa_supplicant_activate_ptk(sm))
+				goto failed;
+		} else if (wpa_supplicant_install_ptk(sm, key,
+						      KEY_FLAG_RX_TX)) {
 			goto failed;
+		}
 	}
 
 	if (key_info & WPA_KEY_INFO_SECURE) {
@@ -2746,6 +2866,7 @@ struct wpa_sm * wpa_sm_init(struct wpa_sm_ctx *ctx)
 		return NULL;
 	dl_list_init(&sm->pmksa_candidates);
 	sm->renew_snonce = 1;
+	sm->keyidx_active = 0;
 	sm->ctx = ctx;
 
 	sm->dot11RSNAConfigPMKLifetime = 43200;
@@ -3164,6 +3285,9 @@ int wpa_sm_set_param(struct wpa_sm *sm, enum wpa_sm_conf_params param,
 	case WPA_PARAM_DENY_PTK0_REKEY:
 		sm->wpa_deny_ptk0_rekey = value;
 		break;
+	case WPA_PARAM_EXTENDED_KEY_ID:
+		sm->extended_key_id = value;
+		break;
 	default:
 		break;
 	}
@@ -3238,6 +3362,18 @@ int wpa_sm_pmf_enabled(struct wpa_sm *sm)
 }
 
 
+int wpa_sm_extended_key_id(struct wpa_sm *sm)
+{
+	return sm->extended_key_id;
+}
+
+
+int wpa_sm_extended_key_id_active(struct wpa_sm *sm)
+{
+	return sm->use_extended_key_id;
+}
+
+
 int wpa_sm_ocv_enabled(struct wpa_sm *sm)
 {
 	struct wpa_ie_data rsn;
@@ -3268,6 +3404,9 @@ int wpa_sm_set_assoc_wpa_ie_default(struct wpa_sm *sm, u8 *wpa_ie,
 
 #ifdef CONFIG_TESTING_OPTIONS
 	if (sm->test_assoc_ie) {
+		struct wpa_eapol_ie_parse ie;
+		struct wpa_ie_data rsn;
+
 		wpa_printf(MSG_DEBUG,
 			   "TESTING: Replace association WPA/RSN IE");
 		if (*wpa_ie_len < wpabuf_len(sm->test_assoc_ie))
@@ -3275,6 +3414,15 @@ int wpa_sm_set_assoc_wpa_ie_default(struct wpa_sm *sm, u8 *wpa_ie,
 		os_memcpy(wpa_ie, wpabuf_head(sm->test_assoc_ie),
 			  wpabuf_len(sm->test_assoc_ie));
 		res = wpabuf_len(sm->test_assoc_ie);
+
+		if (wpa_supplicant_parse_ies(wpa_ie, res, &ie) ||
+		    wpa_parse_wpa_ie_rsn(ie.rsn_ie, ie.rsn_ie_len, &rsn) ||
+		    !(rsn.capabilities &
+		      WPA_CAPABILITY_EXT_KEY_ID_FOR_UNICAST)) {
+			wpa_printf(MSG_DEBUG,
+				   "WPA: Forced own IE disables Extended Key ID");
+			sm->extended_key_id = 0;
+		}
 	} else
 #endif /* CONFIG_TESTING_OPTIONS */
 	res = wpa_gen_wpa_ie(sm, wpa_ie, *wpa_ie_len);
@@ -4253,6 +4401,8 @@ static int fils_ft_build_assoc_req_rsne(struct wpa_sm *sm, struct wpabuf *buf)
 		capab |= WPA_CAPABILITY_MFPR;
 	if (sm->ocv)
 		capab |= WPA_CAPABILITY_OCVC;
+	if (sm->extended_key_id & EXT_KEY_ID_FILS)
+		capab |= WPA_CAPABILITY_EXT_KEY_ID_FOR_UNICAST;
 	wpabuf_put_le16(buf, capab);
 
 	/* PMKID Count */
@@ -4645,6 +4795,7 @@ int fils_process_assoc_resp(struct wpa_sm *sm, const u8 *resp, size_t len)
 		wpa_printf(MSG_DEBUG, "FILS: No GTK KDE");
 		goto fail;
 	}
+
 	maxkeylen = gd.gtk_len = kde.gtk_len - 2;
 	if (wpa_supplicant_check_group_cipher(sm, sm->group_cipher,
 					      gd.gtk_len, maxkeylen,
@@ -4680,11 +4831,21 @@ int fils_process_assoc_resp(struct wpa_sm *sm, const u8 *resp, size_t len)
 			   keylen, (long unsigned int) sm->ptk.tk_len);
 		goto fail;
 	}
+
+	if (elems.rsn_ie) {
+		/* link in RSN for fils_handle_extended_key_id() */
+		kde.rsn_ie = elems.rsn_ie - 2;
+		kde.rsn_ie_len = elems.rsn_ie_len + 2;
+	}
+	if (fils_handle_extended_key_id(sm, &kde))
+		goto fail;
+
 	rsclen = wpa_cipher_rsc_len(sm->pairwise_cipher);
 	wpa_hexdump_key(MSG_DEBUG, "FILS: Set TK to driver",
 			sm->ptk.tk, keylen);
-	if (wpa_sm_set_key(sm, alg, sm->bssid, 0, 1, null_rsc, rsclen,
-			   sm->ptk.tk, keylen, KEY_FLAG_PAIRWISE_RX_TX) < 0) {
+	if (wpa_sm_set_key(sm, alg, sm->bssid, sm->keyidx_active, 1,
+			   null_rsc, rsclen, sm->ptk.tk, keylen,
+			   KEY_FLAG_PAIRWISE_RX_TX) < 0) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
 			"FILS: Failed to set PTK to the driver (alg=%d keylen=%d bssid="
 			MACSTR ")",
diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h
index 0bd14495a..7202125b1 100644
--- a/src/rsn_supp/wpa.h
+++ b/src/rsn_supp/wpa.h
@@ -102,6 +102,7 @@ enum wpa_sm_conf_params {
 	WPA_PARAM_OCV,
 	WPA_PARAM_SAE_PWE,
 	WPA_PARAM_DENY_PTK0_REKEY,
+	WPA_PARAM_EXTENDED_KEY_ID,
 };
 
 struct rsn_supp_config {
@@ -109,6 +110,7 @@ struct rsn_supp_config {
 	int allowed_pairwise_cipher; /* bitfield of WPA_CIPHER_* */
 	int proactive_key_caching;
 	int eap_workaround;
+	enum ext_key_id_support extended_key_id;
 	void *eap_conf_ctx;
 	const u8 *ssid;
 	size_t ssid_len;
@@ -154,6 +156,8 @@ int wpa_sm_set_param(struct wpa_sm *sm, enum wpa_sm_conf_params param,
 int wpa_sm_get_status(struct wpa_sm *sm, char *buf, size_t buflen,
 		      int verbose);
 int wpa_sm_pmf_enabled(struct wpa_sm *sm);
+int wpa_sm_extended_key_id(struct wpa_sm *sm);
+int wpa_sm_extended_key_id_active(struct wpa_sm *sm);
 int wpa_sm_ocv_enabled(struct wpa_sm *sm);
 
 void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise);
@@ -300,6 +304,16 @@ static inline int wpa_sm_pmf_enabled(struct wpa_sm *sm)
 	return 0;
 }
 
+static inline int wpa_sm_extended_key_id(struct wpa_sm *sm)
+{
+	return 0;
+}
+
+static inline int wpa_sm_extended_key_id_active(struct wpa_sm *sm)
+{
+	return 0;
+}
+
 static inline int wpa_sm_ocv_enabled(struct wpa_sm *sm)
 {
 	return 0;
diff --git a/src/rsn_supp/wpa_ft.c b/src/rsn_supp/wpa_ft.c
index 6d627b78f..2caae8334 100644
--- a/src/rsn_supp/wpa_ft.c
+++ b/src/rsn_supp/wpa_ft.c
@@ -263,6 +263,8 @@ static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len,
 		capab |= WPA_CAPABILITY_MFPR;
 	if (sm->ocv)
 		capab |= WPA_CAPABILITY_OCVC;
+	if (sm->extended_key_id & EXT_KEY_ID_FT0)
+		capab |= WPA_CAPABILITY_EXT_KEY_ID_FOR_UNICAST;
 	WPA_PUT_LE16(pos, capab);
 	pos += 2;
 
@@ -429,8 +431,8 @@ static int wpa_ft_install_ptk(struct wpa_sm *sm, const u8 *bssid)
 	alg = wpa_cipher_to_alg(sm->pairwise_cipher);
 	keylen = wpa_cipher_key_len(sm->pairwise_cipher);
 
-	if (wpa_sm_set_key(sm, alg, bssid, 0, 1, null_rsc, sizeof(null_rsc),
-			   (u8 *) sm->ptk.tk, keylen,
+	if (wpa_sm_set_key(sm, alg, bssid, sm->keyidx_active, 1, null_rsc,
+			   sizeof(null_rsc), (u8 *) sm->ptk.tk, keylen,
 			   KEY_FLAG_PAIRWISE_RX_TX) < 0) {
 		wpa_printf(MSG_WARNING, "FT: Failed to set PTK to the driver");
 		return -1;
@@ -440,6 +442,26 @@ static int wpa_ft_install_ptk(struct wpa_sm *sm, const u8 *bssid)
 }
 
 
+static void ft_handle_extended_key_id(struct wpa_sm *sm,
+				      struct wpa_ft_ies *parse)
+{
+	if (sm->extended_key_id & EXT_KEY_ID_FT0 &&
+	    parse->rsn && sm->pairwise_cipher != WPA_CIPHER_TKIP &&
+	    parse->rsn_capab & WPA_CAPABILITY_EXT_KEY_ID_FOR_UNICAST) {
+		wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+			"FT: Using Extended Key ID");
+		sm->use_extended_key_id = 1;
+	} else {
+		sm->use_extended_key_id = 0;
+	}
+	/* There is no standardized way to hand over the KeyID in FT.
+	 * Since FT can only be used for the initial connect we simply
+	 * assume it must be always 0.
+	 */
+	sm->keyidx_active = 0;
+}
+
+
 /**
  * wpa_ft_prepare_auth_request - Generate over-the-air auth request
  * @sm: Pointer to WPA state machine data from wpa_sm_init()
@@ -661,6 +683,7 @@ int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len,
 		os_free(ft_ies);
 	}
 
+	ft_handle_extended_key_id(sm, &parse);
 	wpa_sm_mark_authenticated(sm, bssid);
 	ret = wpa_ft_install_ptk(sm, bssid);
 	if (ret) {
diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h
index 7af678dcd..41c57e3a8 100644
--- a/src/rsn_supp/wpa_i.h
+++ b/src/rsn_supp/wpa_i.h
@@ -26,6 +26,7 @@ struct wpa_sm {
 	u8 snonce[WPA_NONCE_LEN];
 	u8 anonce[WPA_NONCE_LEN]; /* ANonce from the last 1/4 msg */
 	int renew_snonce;
+	int keyidx_active;
 	u8 rx_replay_counter[WPA_REPLAY_COUNTER_LEN];
 	int rx_replay_counter_set;
 	u8 request_counter[WPA_REPLAY_COUNTER_LEN];
@@ -68,6 +69,8 @@ struct wpa_sm {
 	int wpa_rsc_relaxation;
 	int owe_ptk_workaround;
 	int beacon_prot;
+	enum ext_key_id_support extended_key_id;
+	int use_extended_key_id;
 
 	u8 own_addr[ETH_ALEN];
 	const char *ifname;
diff --git a/src/rsn_supp/wpa_ie.c b/src/rsn_supp/wpa_ie.c
index 03c0d7e85..ed0e1f6c7 100644
--- a/src/rsn_supp/wpa_ie.c
+++ b/src/rsn_supp/wpa_ie.c
@@ -221,6 +221,13 @@ static int wpa_gen_wpa_ie_rsn(u8 *rsn_ie, size_t rsn_ie_len,
 		capab |= WPA_CAPABILITY_MFPR;
 	if (sm->ocv)
 		capab |= WPA_CAPABILITY_OCVC;
+	if (sm->extended_key_id &&
+	    (!(wpa_key_mgmt_ft(key_mgmt) || wpa_key_mgmt_fils(key_mgmt)) ||
+	     (wpa_key_mgmt_ft(key_mgmt) &&
+	      sm->extended_key_id & EXT_KEY_ID_FT0) ||
+	     (wpa_key_mgmt_fils(key_mgmt) &&
+	      sm->extended_key_id & EXT_KEY_ID_FILS)))
+		capab |= WPA_CAPABILITY_EXT_KEY_ID_FOR_UNICAST;
 	WPA_PUT_LE16(pos, capab);
 	pos += 2;
 
diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c
index 87573ef10..5d227519c 100644
--- a/wpa_supplicant/ap.c
+++ b/wpa_supplicant/ap.c
@@ -346,6 +346,7 @@ static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s,
 	bss->isolate = !wpa_s->conf->p2p_intra_bss;
 	bss->force_per_enrollee_psk = wpa_s->global->p2p_per_sta_psk;
 	bss->wpa_deny_ptk0_rekey = ssid->wpa_deny_ptk0_rekey;
+	bss->extended_key_id = ssid->extended_key_id;
 
 	if (ssid->p2p_group) {
 		os_memcpy(bss->ip_addr_go, wpa_s->p2pdev->conf->ip_addr_go, 4);
diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
index 2b9c3f53e..4de450aa6 100644
--- a/wpa_supplicant/config.c
+++ b/wpa_supplicant/config.c
@@ -1147,6 +1147,54 @@ static char * wpa_config_write_pairwise(const struct parse_data *data,
 #endif /* NO_CONFIG_WRITE */
 
 
+static int wpa_config_parse_extended_key_id(const struct parse_data *data,
+					    struct wpa_ssid *ssid, int line,
+					    const char *value)
+{
+#ifdef CONFIG_NO_WPA
+	return -1;
+#else /* CONFIG_NO_WPA */
+	int val = wpa_parse_extended_key_id(value);
+	if (val < 0) {
+		wpa_printf(MSG_ERROR, "Line %d: extended_key_id=%s invalid",
+			   line, value);
+		return -1;
+	}
+
+	ssid->extended_key_id = val;
+	return 0;
+#endif /* CONFIG_NO_WPA */
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_extended_key_id(const struct parse_data *data,
+					       struct wpa_ssid *ssid)
+{
+#ifdef CONFIG_NO_WPA
+	return NULL;
+#else /* CONFIG_NO_WPA */
+	char *buf;
+
+	if (ssid->extended_key_id == EXT_KEY_ID_DEFAULT)
+		return NULL;
+
+	buf = os_zalloc(30);
+	if (buf == NULL)
+		return NULL;
+
+	if (wpa_write_extended_key_id(buf, buf + 30,
+				      ssid->extended_key_id) < 0) {
+		os_free(buf);
+		return NULL;
+	}
+
+	return buf;
+#endif /* CONFIG_NO_WPA */
+}
+#endif /* NO_CONFIG_WRITE */
+
+
 static int wpa_config_parse_group(const struct parse_data *data,
 				  struct wpa_ssid *ssid, int line,
 				  const char *value)
@@ -2502,6 +2550,7 @@ static const struct parse_data ssid_fields[] = {
 #endif /* CONFIG_MESH */
 	{ INT(wpa_ptk_rekey) },
 	{ INT_RANGE(wpa_deny_ptk0_rekey, 0, 2) },
+	{ FUNC(extended_key_id) },
 	{ INT(group_rekey) },
 	{ STR(bgscan) },
 	{ INT_RANGE(ignore_broadcast_ssid, 0, 2) },
@@ -3028,6 +3077,7 @@ void wpa_config_set_network_defaults(struct wpa_ssid *ssid)
 	ssid->group_cipher = DEFAULT_GROUP;
 	ssid->key_mgmt = DEFAULT_KEY_MGMT;
 	ssid->wpa_deny_ptk0_rekey = PTK0_REKEY_ALLOW_ALWAYS;
+	ssid->extended_key_id = EXT_KEY_ID_DEFAULT;
 	ssid->bg_scan_period = DEFAULT_BG_SCAN_PERIOD;
 	ssid->ht = 1;
 #ifdef IEEE8021X_EAPOL
diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c
index b8e56f5b2..69c428603 100644
--- a/wpa_supplicant/config_file.c
+++ b/wpa_supplicant/config_file.c
@@ -921,6 +921,7 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid)
 #endif /* CONFIG_MESH */
 	INT(wpa_ptk_rekey);
 	INT(wpa_deny_ptk0_rekey);
+	STR(extended_key_id);
 	INT(group_rekey);
 	INT(ignore_broadcast_ssid);
 #ifdef CONFIG_DPP
diff --git a/wpa_supplicant/config_ssid.h b/wpa_supplicant/config_ssid.h
index 24c7a3d9b..fbe865b9b 100644
--- a/wpa_supplicant/config_ssid.h
+++ b/wpa_supplicant/config_ssid.h
@@ -547,6 +547,14 @@ struct wpa_ssid {
 	unsigned int vht_center_freq1;
 	unsigned int vht_center_freq2;
 
+	/** extended_key_id - Extended Key ID support
+	 *
+	 * IEEE 802.11-2016 optionally allows to use keyid 0 and 1 for PTK keys
+	 * with Extended Key ID. This variable controls if and when we want to
+	 * use it.
+	 */
+	enum ext_key_id_support extended_key_id;
+
 	/**
 	 * wpa_ptk_rekey - Maximum lifetime for PTK in seconds
 	 *
diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
index 077bd6449..664dfa7ed 100644
--- a/wpa_supplicant/ctrl_iface.c
+++ b/wpa_supplicant/ctrl_iface.c
@@ -5366,6 +5366,9 @@ static void wpa_supplicant_ctrl_iface_drop_sa(struct wpa_supplicant *wpa_s)
 
 	wpa_drv_set_key(wpa_s, WPA_ALG_NONE, wpa_s->bssid, 0, 0, NULL, 0, NULL,
 			0, KEY_FLAG_PAIRWISE);
+	if (wpa_sm_extended_key_id(wpa_s->wpa))
+		wpa_drv_set_key(wpa_s, WPA_ALG_NONE, wpa_s->bssid, 1, 0,
+				NULL, 0, NULL, 0, KEY_FLAG_PAIRWISE);
 	/* MLME-SETPROTECTION.request(None) */
 	wpa_drv_mlme_setprotection(wpa_s, wpa_s->bssid,
 				   MLME_SETPROTECTION_PROTECT_TYPE_NONE,
diff --git a/wpa_supplicant/dbus/dbus_new_handlers.c b/wpa_supplicant/dbus/dbus_new_handlers.c
index c842c50e9..e0eddb570 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers.c
+++ b/wpa_supplicant/dbus/dbus_new_handlers.c
@@ -991,7 +991,7 @@ dbus_bool_t wpas_dbus_getter_global_capabilities(
 	const struct wpa_dbus_property_desc *property_desc,
 	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
-	const char *capabilities[11];
+	const char *capabilities[12];
 	size_t num_items = 0;
 #ifdef CONFIG_FILS
 	struct wpa_global *global = user_data;
@@ -1037,6 +1037,7 @@ dbus_bool_t wpas_dbus_getter_global_capabilities(
 #ifdef CONFIG_OWE
 	capabilities[num_items++] = "owe";
 #endif /* CONFIG_OWE */
+	capabilities[num_items++] = "extended-key-id";
 
 	return wpas_dbus_simple_array_property_getter(iter,
 						      DBUS_TYPE_STRING,
diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h
index d3fb58707..edde6fa99 100644
--- a/wpa_supplicant/driver_i.h
+++ b/wpa_supplicant/driver_i.h
@@ -165,7 +165,14 @@ static inline int wpa_drv_set_key(struct wpa_supplicant *wpa_s,
 	params.key_flag = key_flag;
 
 	if (alg != WPA_ALG_NONE) {
-		if (key_idx >= 0 && key_idx <= 6)
+		/* keyidx = 1 can be either a broadcast or - with
+		 * Extended Key ID - an unicast key. Use bit 15 for
+		 * the pairwise keyidx 1 which is hopefully high enough
+		 * to not clash with future extensions.
+		 */
+		if (key_idx == 1 && key_flag & KEY_FLAG_PAIRWISE)
+			wpa_s->keys_cleared &= ~BIT(15);
+		else if (key_idx >= 0 && key_idx <= 5)
 			wpa_s->keys_cleared &= ~BIT(key_idx);
 		else
 			wpa_s->keys_cleared = 0;
diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c
index 22885e646..14cadcedc 100644
--- a/wpa_supplicant/wpa_cli.c
+++ b/wpa_supplicant/wpa_cli.c
@@ -1441,7 +1441,7 @@ static const char *network_fields[] = {
 	"dot11MeshHoldingTimeout",
 #endif /* CONFIG_MESH */
 	"wpa_ptk_rekey", "bgscan", "ignore_broadcast_ssid",
-	"wpa_deny_ptk0_rekey",
+	"wpa_deny_ptk0_rekey", "extended_key_id",
 	"enable_edmg", "edmg_channel",
 #ifdef CONFIG_P2P
 	"go_p2p_dev_addr", "p2p_client_list", "psk_list",
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index 44c34f041..5f8a108c0 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -746,10 +746,15 @@ void wpa_clear_keys(struct wpa_supplicant *wpa_s, const u8 *addr)
 		wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, i, 0, NULL, 0,
 				NULL, 0, KEY_FLAG_GROUP);
 	}
-	if (!(wpa_s->keys_cleared & BIT(0)) && addr &&
+	/* Pairwise key idx 1 for Extended Key ID is tracked in bit 15 */
+	if (~wpa_s->keys_cleared & (BIT(0) | BIT(15)) && addr &&
 	    !is_zero_ether_addr(addr)) {
-		wpa_drv_set_key(wpa_s, WPA_ALG_NONE, addr, 0, 0, NULL, 0, NULL,
-				0, KEY_FLAG_PAIRWISE);
+		if (!(wpa_s->keys_cleared & (BIT(0))))
+			wpa_drv_set_key(wpa_s, WPA_ALG_NONE, addr, 0, 0, NULL,
+					0, NULL, 0, KEY_FLAG_PAIRWISE);
+		if (!(wpa_s->keys_cleared & (BIT(15))))
+			wpa_drv_set_key(wpa_s, WPA_ALG_NONE, addr, 1, 0, NULL,
+					0, NULL, 0, KEY_FLAG_PAIRWISE);
 		/* MLME-SETPROTECTION.request(None) */
 		wpa_drv_mlme_setprotection(
 			wpa_s, addr,
@@ -1633,6 +1638,19 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
 		sae_pwe = 1;
 	wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_SAE_PWE, sae_pwe);
 
+	/* Extended Key ID is only supported in INFRA mode so far */
+	if (ssid->mode == WPAS_MODE_INFRA && ssid->extended_key_id &&
+	    ssid->proto & WPA_PROTO_RSN &&
+	    ssid->pairwise_cipher & (WPA_CIPHER_CCMP | WPA_CIPHER_CCMP_256 |
+				     WPA_CIPHER_GCMP | WPA_CIPHER_GCMP_256) &&
+	    wpa_s->drv_flags & WPA_DRIVER_FLAGS_EXTENDED_KEY_ID) {
+		wpa_msg(wpa_s, MSG_INFO, "Enable Extended Key ID support");
+		wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_EXTENDED_KEY_ID,
+				 ssid->extended_key_id);
+	} else {
+		wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_EXTENDED_KEY_ID, 0);
+	}
+
 	if (wpa_sm_set_assoc_wpa_ie_default(wpa_s->wpa, wpa_ie, wpa_ie_len)) {
 		wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to generate WPA IE");
 		return -1;
diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf
index f3a750e3c..18ba69eb4 100644
--- a/wpa_supplicant/wpa_supplicant.conf
+++ b/wpa_supplicant/wpa_supplicant.conf
@@ -1098,6 +1098,44 @@ fast_reauth=1
 # hex without quotation, e.g., 0102030405)
 # wep_tx_keyidx: Default WEP key index (TX) (0..3)
 #
+# extended_key_id:
+# "Extended Key ID support for Individually Addressed Frames" according to
+# IEEE 802.11-2016.
+#
+# Extended Key ID allows to rekey PTK keys without the impacts PTK0 rekeying
+# has. It can only be used when the driver supports it and requires wpa=2
+# with a CCMP/GCMP cipher.
+#
+# The standard is not regulating if or how Extended Key ID can be used in
+# combination with FT or FILS. Enabling it for FT or FILS may therefore be
+# incompatible with other implementations.
+#
+# Available options, can be combined with '+':
+# OFF           : Disable Extended Key ID support
+# BASIC         : Enable core Extended Key ID support according to
+#		  IEEE 802.11-2016 when the driver supports it
+# PREFER0       : Use key 0 for the initial key when either 0 or 1 can be used
+#		  in AP mode and otherwise ignored
+# FT0           : - NOT PART OF IEEE 802.11-2016 -
+#		  Enable Extended Key ID for FT and assume keyid 0 for all FT
+#		  handshakes
+# FILS0         : - NOT PART OF IEEE 802.11-2016 -
+#		  Enable Extended Key ID with FILS and assume keyid 0 for all
+#		  FILS handshakes (detects and use FILS_CUSTOM when needed)
+# FILS_CUSTOM   : - NOT PART OF IEEE 802.11-2016 -
+#		  Enable Extended Key ID with FILS and add the keyid to the
+#		  FILS handshakes (detects and use FILS0 when needed)
+# Rules:
+# - Either OFF or BASIC must be defined
+# - BASIC can be combined with anything except OFF
+# - FILS0 and FILS_CUSTOM are mutually exclusive
+#
+# Example:
+# extended_key_id=BASIC+FT0+FILS0
+#
+# Default:
+# extended_key_id=BASIC
+#
 # wpa_ptk_rekey: Maximum lifetime for PTK in seconds. This can be used to
 # enforce rekeying of PTK to mitigate some attacks against TKIP deficiencies.
 #
diff --git a/wpa_supplicant/wpas_glue.c b/wpa_supplicant/wpas_glue.c
index 39b05b2b9..574a3a827 100644
--- a/wpa_supplicant/wpas_glue.c
+++ b/wpa_supplicant/wpas_glue.c
@@ -533,7 +533,8 @@ static int wpa_supplicant_set_key(void *_wpa_s, enum wpa_alg alg,
 	}
 #endif /* CONFIG_TESTING_GET_GTK */
 #ifdef CONFIG_TESTING_OPTIONS
-	if (addr && !is_broadcast_ether_addr(addr)) {
+	if (addr && !is_broadcast_ether_addr(addr) &&
+	    !(key_flag & KEY_FLAG_MODIFY)) {
 		wpa_s->last_tk_alg = alg;
 		os_memcpy(wpa_s->last_tk_addr, addr, ETH_ALEN);
 		wpa_s->last_tk_key_idx = key_idx;
@@ -1077,7 +1078,8 @@ static int wpa_supplicant_eap_auth_start_cb(void *ctx)
 {
 	struct wpa_supplicant *wpa_s = ctx;
 
-	if (!wpa_s->new_connection && wpa_s->deny_ptk0_rekey) {
+	if (!wpa_s->new_connection && wpa_s->deny_ptk0_rekey &&
+	    !wpa_sm_extended_key_id_active(wpa_s->wpa)) {
 		wpa_msg(wpa_s, MSG_INFO,
 			"WPA: PTK0 rekey not allowed, reconnecting");
 		wpa_supplicant_reconnect(wpa_s);
@@ -1318,6 +1320,7 @@ void wpa_supplicant_rsn_supp_set_config(struct wpa_supplicant *wpa_s,
 #endif /* IEEE8021X_EAPOL */
 		conf.ssid = ssid->ssid;
 		conf.ssid_len = ssid->ssid_len;
+		conf.extended_key_id = ssid->extended_key_id;
 		conf.wpa_ptk_rekey = ssid->wpa_ptk_rekey;
 		conf.wpa_deny_ptk0_rekey = ssid->wpa_deny_ptk0_rekey;
 		conf.owe_ptk_workaround = ssid->owe_ptk_workaround;
-- 
2.25.1


_______________________________________________
Hostap mailing list
Hostap@xxxxxxxxxxxxxxxxxxx
http://lists.infradead.org/mailman/listinfo/hostap



[Index of Archives]     [Linux Wireless]     [Linux Kernel]     [ATH6KL]     [Linux Bluetooth]     [Linux Netdev]     [Kernel Newbies]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Samba]     [Device Mapper]

  Powered by Linux