diff mbox

[36/44] FT: include identity and radius_cui in pull/resp frames

Message ID 1456314830-12935-37-git-send-email-michael-dev@fami-braun.de
State Changes Requested
Headers show

Commit Message

michael-dev Feb. 24, 2016, 11:53 a.m. UTC
From: Michael Braun <michael-dev@fami-braun.de>

Signed-off-by: Michael Braun <michael-dev@fami-braun.de>
---
 src/ap/ieee802_11.c       |   4 +
 src/ap/wpa_auth.h         |  28 ++++++-
 src/ap/wpa_auth_ft.c      | 186 +++++++++++++++++++++++++++++++++++++++++-----
 src/ap/wpa_auth_glue.c    | 150 +++++++++++++++++++++++++++++++++++++
 tests/hwsim/test_ap_ft.py |   9 ++-
 5 files changed, 357 insertions(+), 20 deletions(-)

Comments

Jouni Malinen Feb. 27, 2016, 8:20 p.m. UTC | #1
On Wed, Feb 24, 2016 at 12:53:42PM +0100, michael-dev@fami-braun.de wrote:
> diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
> @@ -957,8 +957,12 @@ int handle_auth_cfg_sta(struct hostapd_data *hapd, struct sta_info *sta,
> +	if (sta->identity)
> +		os_free(sta->identity);

os_free(NULL) is fine. There should be no if (ptr) before it.

> diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
> @@ -93,13 +98,19 @@ struct ft_r0kh_r1kh_resp_frame {
>  	le16 pairwise;
>  	le16 expiresIn; /* 0xffff for no-entry */
>  	struct ft_vlan vlan;
> +	u8 identity_len;
> +	u8 identity[FT_IDENTITY_LEN];
> +	u8 radius_cui_len;
> +	u8 radius_cui[FT_RADIUS_CUI_LEN];
>  	u8 pad[FT_R0KH_R1KH_RESP_PAD_LEN]; /* 8-octet boundary for AES block */
>  	u8 key_wrap_extra[8];
>  } STRUCT_PACKED;

Wouldn't this break compatibility with older hostapd versions? This is
supposed to be a protocol definition and changes should be done in a
manner that maintains backwards compatibility. That said, I would not be
surprised if it is impossible to extend the current protocol design
here, so this may require changes that come up with a new AP-to-AP
protocol that is done in a way that can be extended without breaking
compatibility with older versions.


> diff --git a/tests/hwsim/test_ap_ft.py b/tests/hwsim/test_ap_ft.py

Please keep tests/* changes in separate commits. There are use cases
where hostap.git commits are merged into trees that do not include the
tests directory structure and even without that, it is more convenient
to git cherry-pick implementation changes without having to depend on
the tests/hwsim/* files being up-to-date.
diff mbox

Patch

diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index 7bf6d2e..0486923 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -957,8 +957,12 @@  int handle_auth_cfg_sta(struct hostapd_data *hapd, struct sta_info *sta,
 		sta->psk = NULL;
 	}
 
+	if (sta->identity)
+		os_free(sta->identity);
 	sta->identity = info->identity;
 	info->identity = NULL;
+	if (sta->radius_cui)
+		os_free(sta->radius_cui);
 	sta->radius_cui = info->radius_cui;
 	info->radius_cui = NULL;
 
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
index b73c046..3b27760 100644
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -45,6 +45,9 @@  struct ft_rrb_frame {
 #define FT_MAX_NUM_TAGGED_VLAN 32
 #define FT_VLAN_DATA_LEN sizeof(struct ft_vlan)
 
+#define FT_IDENTITY_LEN 256
+#define FT_RADIUS_CUI_LEN 256
+
 #define FT_R0KH_R1KH_PULL_NONCE_LEN 16
 #define FT_R0KH_R1KH_PULL_DATA_LEN (FT_R0KH_R1KH_PULL_NONCE_LEN + \
 				    FT_R0KH_ID_MAX_LEN + 1 + \
@@ -77,7 +80,9 @@  struct ft_r0kh_r1kh_pull_frame {
 
 #define FT_R0KH_R1KH_RESP_DATA_LEN (FT_R0KH_R1KH_PULL_NONCE_LEN + \
 				    FT_R1KH_ID_LEN + ETH_ALEN + PMK_LEN + \
-				    WPA_PMK_NAME_LEN + FT_VLAN_DATA_LEN + 2 + 2)
+				    WPA_PMK_NAME_LEN + 2 + 2 + \
+				    FT_VLAN_DATA_LEN + 1 + FT_IDENTITY_LEN + \
+				    1 + FT_RADIUS_CUI_LEN)
 #define FT_R0KH_R1KH_RESP_PAD_LEN (8 - FT_R0KH_R1KH_RESP_DATA_LEN % 8)
 struct ft_r0kh_r1kh_resp_frame {
 	u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */
@@ -93,13 +98,19 @@  struct ft_r0kh_r1kh_resp_frame {
 	le16 pairwise;
 	le16 expiresIn; /* 0xffff for no-entry */
 	struct ft_vlan vlan;
+	u8 identity_len;
+	u8 identity[FT_IDENTITY_LEN];
+	u8 radius_cui_len;
+	u8 radius_cui[FT_RADIUS_CUI_LEN];
 	u8 pad[FT_R0KH_R1KH_RESP_PAD_LEN]; /* 8-octet boundary for AES block */
 	u8 key_wrap_extra[8];
 } STRUCT_PACKED;
 
 #define FT_R0KH_R1KH_PUSH_DATA_LEN (4 + FT_R1KH_ID_LEN + ETH_ALEN + \
 				    WPA_PMK_NAME_LEN + PMK_LEN + \
-				    WPA_PMK_NAME_LEN + FT_VLAN_DATA_LEN + 2 + 2)
+				    WPA_PMK_NAME_LEN + 2 + 2 + \
+				    FT_VLAN_DATA_LEN + 1 + FT_IDENTITY_LEN + \
+				    1 + FT_RADIUS_CUI_LEN)
 #define FT_R0KH_R1KH_PUSH_PAD_LEN (8 - FT_R0KH_R1KH_PUSH_DATA_LEN % 8)
 struct ft_r0kh_r1kh_push_frame {
 	u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */
@@ -118,6 +129,10 @@  struct ft_r0kh_r1kh_push_frame {
 	le16 pairwise;
 	le16 expiresIn;
 	struct ft_vlan vlan;
+	u8 identity_len;
+	u8 identity[FT_IDENTITY_LEN];
+	u8 radius_cui_len;
+	u8 radius_cui[FT_RADIUS_CUI_LEN];
 	u8 pad[FT_R0KH_R1KH_PUSH_PAD_LEN]; /* 8-octet boundary for AES block */
 	u8 key_wrap_extra[8];
 } STRUCT_PACKED;
@@ -251,6 +266,15 @@  struct wpa_auth_callbacks {
 		       void *cb_ctx, int cb_ctx_len);
 	int (*set_vlan)(void *ctx, const u8 *sta_addr, struct ft_vlan *vlan);
 	int (*get_vlan)(void *ctx, const u8 *sta_addr, struct ft_vlan *vlan);
+	size_t (*get_identity)(void *ctx, const u8 *sta_addr, u8 *buf,
+			       size_t buflen);
+	size_t (*get_radius_cui)(void *ctx, const u8 *sta_addr, u8 *buf,
+				 size_t buflen);
+	void (*set_identity)(void *ctx, const u8 *sta_addr, u8 *identity,
+			     size_t identity_len);
+	void (*set_radius_cui)(void *ctx, const u8 *sta_addr, u8 *radius_cui,
+			       size_t radius_cui_len);
+
 	int (*send_ft_action)(void *ctx, const u8 *dst,
 			      const u8 *data, size_t data_len);
 	int (*add_tspec)(void *ctx, const u8 *sta_addr, u8 *tspec_ie,
diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c
index 7dff527..2f68d45 100644
--- a/src/ap/wpa_auth_ft.c
+++ b/src/ap/wpa_auth_ft.c
@@ -103,6 +103,50 @@  wpa_ft_get_vlan(struct wpa_authenticator *wpa_auth, const u8 *sta_addr,
 }
 
 
+static size_t
+wpa_ft_get_identity(struct wpa_authenticator *wpa_auth, const u8 *sta_addr,
+		     u8 *buf, size_t buflen)
+{
+	if (wpa_auth->cb.get_identity == NULL)
+		return 0;
+	return wpa_auth->cb.get_identity(wpa_auth->cb.ctx, sta_addr, buf,
+					 buflen);
+}
+
+
+static size_t
+wpa_ft_get_radius_cui(struct wpa_authenticator *wpa_auth, const u8 *sta_addr,
+		       u8 *buf, size_t buflen)
+{
+	if (wpa_auth->cb.get_radius_cui == NULL)
+		return 0;
+	return wpa_auth->cb.get_radius_cui(wpa_auth->cb.ctx, sta_addr, buf,
+					   buflen);
+}
+
+
+static void
+wpa_ft_set_identity(struct wpa_authenticator *wpa_auth, const u8 *sta_addr,
+		    u8 *identity, size_t identity_len)
+{
+	if (wpa_auth->cb.set_identity == NULL)
+		return;
+	wpa_auth->cb.set_identity(wpa_auth->cb.ctx, sta_addr, identity,
+				  identity_len);
+}
+
+
+static void
+wpa_ft_set_radius_cui(struct wpa_authenticator *wpa_auth, const u8 *sta_addr,
+		      u8 *radius_cui, size_t radius_cui_len)
+{
+	if (wpa_auth->cb.set_radius_cui == NULL)
+		return;
+	wpa_auth->cb.set_radius_cui(wpa_auth->cb.ctx, sta_addr, radius_cui,
+				    radius_cui_len);
+}
+
+
 static int wpa_ft_add_tspec(struct wpa_authenticator *wpa_auth,
 			    const u8 *sta_addr,
 			    u8 *tspec_ie, size_t tspec_ielen)
@@ -193,7 +237,11 @@  struct wpa_ft_pmk_r0_sa {
 	int pairwise; /* Pairwise cipher suite, WPA_CIPHER_* */
 	struct ft_vlan vlan;
 	os_time_t expiration; /* 0 for no expiration */
-	/* TODO: identity, radius_class, EAP type, expiration from EAPOL */
+	u8 identity[FT_IDENTITY_LEN];
+	u8 identity_len;
+	u8 radius_cui[FT_RADIUS_CUI_LEN];
+	u8 radius_cui_len;
+	/* TODO: radius_class, EAP type, expiration from EAPOL */
 	int pmk_r1_pushed;
 };
 
@@ -204,7 +252,11 @@  struct wpa_ft_pmk_r1_sa {
 	u8 spa[ETH_ALEN];
 	int pairwise; /* Pairwise cipher suite, WPA_CIPHER_* */
 	struct ft_vlan vlan;
-	/* TODO: expiration, identity, radius_class, EAP type */
+	u8 identity[FT_IDENTITY_LEN];
+	u8 identity_len;
+	u8 radius_cui[FT_RADIUS_CUI_LEN];
+	u8 radius_cui_len;
+	/* TODO: expiration from EAPOL, radius_class, EAP type */
 };
 
 struct wpa_ft_pmk_cache {
@@ -313,7 +365,9 @@  static int wpa_ft_store_pmk_r0(struct wpa_authenticator *wpa_auth,
 			       const u8 *spa, const u8 *pmk_r0,
 			       const u8 *pmk_r0_name, int pairwise,
 			       const struct ft_vlan vlan,
-			       const int expiresIn)
+			       const int expiresIn,
+			       const u8 *identity, u8 identity_len,
+			       const u8 *radius_cui, u8 radius_cui_len)
 {
 	struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache;
 	struct wpa_ft_pmk_r0_sa *r0;
@@ -333,6 +387,14 @@  static int wpa_ft_store_pmk_r0(struct wpa_authenticator *wpa_auth,
 	os_memcpy(&r0->vlan, &vlan, FT_VLAN_DATA_LEN);
 	if (expiresIn > 0)
 		r0->expiration = now.sec + expiresIn;
+	if (identity && identity_len <= FT_IDENTITY_LEN) {
+		os_memcpy(r0->identity, identity, identity_len);
+		r0->identity_len = identity_len;
+	}
+	if (radius_cui && radius_cui_len <= FT_RADIUS_CUI_LEN) {
+		os_memcpy(r0->radius_cui, radius_cui, radius_cui_len);
+		r0->radius_cui_len = radius_cui_len;
+	}
 
 	dl_list_add(&cache->pmk_r0, &r0->list);
 
@@ -344,10 +406,16 @@  static int wpa_ft_store_pmk_r0(struct wpa_authenticator *wpa_auth,
 }
 
 
+/*
+ * identity and radius_cui need to be FT_IDENTITY_LEN / FT_RADIUS_CUI_LEN big
+ * at least
+ */
 static int wpa_ft_fetch_pmk_r0(struct wpa_authenticator *wpa_auth,
 			       const u8 *spa, const u8 *pmk_r0_name,
 			       u8 *pmk_r0, int *pairwise,
-			       struct ft_vlan *vlan, int *expiresIn)
+			       struct ft_vlan *vlan, int *expiresIn,
+			       u8 *identity, u8 *identity_len,
+			       u8 *radius_cui, u8 *radius_cui_len)
 {
 	struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache;
 	struct wpa_ft_pmk_r0_sa *r0;
@@ -369,6 +437,16 @@  static int wpa_ft_fetch_pmk_r0(struct wpa_authenticator *wpa_auth,
 				*expiresIn = 1;
 			else
 				*expiresIn = 0;
+			if (identity && identity_len) {
+				os_memcpy(identity, r0->identity,
+					  r0->identity_len);
+				*identity_len = r0->identity_len;
+			}
+			if (radius_cui && radius_cui_len) {
+				os_memcpy(radius_cui, r0->radius_cui,
+					  r0->radius_cui_len);
+				*radius_cui_len = r0->radius_cui_len;
+			}
 
 			return 0;
 		}
@@ -382,7 +460,9 @@  static int wpa_ft_store_pmk_r1(struct wpa_authenticator *wpa_auth,
 			       const u8 *spa, const u8 *pmk_r1,
 			       const u8 *pmk_r1_name, int pairwise,
 			       const struct ft_vlan vlan,
-			       int expiresIn)
+			       int expiresIn,
+			       const u8 *identity, u8 identity_len,
+			       const u8 *radius_cui, u8 radius_cui_len)
 {
 	struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache;
 	struct wpa_ft_pmk_r1_sa *r1;
@@ -398,6 +478,14 @@  static int wpa_ft_store_pmk_r1(struct wpa_authenticator *wpa_auth,
 	os_memcpy(r1->spa, spa, ETH_ALEN);
 	r1->pairwise = pairwise;
 	os_memcpy(&r1->vlan, &vlan, FT_VLAN_DATA_LEN);
+	if (identity && identity_len <= FT_IDENTITY_LEN) {
+		os_memcpy(r1->identity, identity, identity_len);
+		r1->identity_len = identity_len;
+	}
+	if (radius_cui && radius_cui_len <= FT_RADIUS_CUI_LEN) {
+		os_memcpy(r1->radius_cui, radius_cui, radius_cui_len);
+		r1->radius_cui_len = radius_cui_len;
+	}
 
 	dl_list_add(&cache->pmk_r1, &r1->list);
 
@@ -412,7 +500,9 @@  static int wpa_ft_store_pmk_r1(struct wpa_authenticator *wpa_auth,
 static int wpa_ft_fetch_pmk_r1(struct wpa_authenticator *wpa_auth,
 			       const u8 *spa, const u8 *pmk_r1_name,
 			       u8 *pmk_r1, int *pairwise,
-			       struct ft_vlan *vlan)
+			       struct ft_vlan *vlan,
+			       u8 *identity, u8 *identity_len,
+			       u8 *radius_cui, u8 *radius_cui_len)
 {
 	struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache;
 	struct wpa_ft_pmk_r1_sa *r1;
@@ -426,6 +516,16 @@  static int wpa_ft_fetch_pmk_r1(struct wpa_authenticator *wpa_auth,
 				*pairwise = r1->pairwise;
 			if (vlan)
 				os_memcpy(vlan, &r1->vlan, FT_VLAN_DATA_LEN);
+			if (identity && identity_len) {
+				os_memcpy(identity, r1->identity,
+					  r1->identity_len);
+				*identity_len = r1->identity_len;
+			}
+			if (radius_cui && radius_cui_len) {
+				os_memcpy(radius_cui, r1->radius_cui,
+					  r1->radius_cui_len);
+				*radius_cui_len = r1->radius_cui_len;
+			}
 			return 0;
 		}
 	}
@@ -676,6 +776,8 @@  int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk,
 	int psk_local = sm->wpa_auth->conf.ft_psk_generate_local;
 	int expiresIn = sm->wpa_auth->conf.r0_key_lifetime * 60;
 	struct ft_vlan vlan;
+	u8 identity[FT_IDENTITY_LEN], radius_cui[FT_RADIUS_CUI_LEN];
+	int identity_len, radius_cui_len;
 
 	if (sm->xxkey_len == 0) {
 		wpa_printf(MSG_DEBUG, "FT: XXKey not available for key "
@@ -688,6 +790,10 @@  int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk,
 			   MAC2STR(sm->addr));
 		return -1;
 	}
+	identity_len = wpa_ft_get_identity(sm->wpa_auth, sm->addr, identity,
+					   sizeof(identity));
+	radius_cui_len = wpa_ft_get_radius_cui(sm->wpa_auth, sm->addr,
+					       radius_cui, sizeof(radius_cui));
 
 	wpa_derive_pmk_r0(sm->xxkey, sm->xxkey_len, ssid, ssid_len, mdid,
 			  r0kh, r0kh_len, sm->addr, pmk_r0, pmk_r0_name);
@@ -695,8 +801,9 @@  int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk,
 	wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name", pmk_r0_name, WPA_PMK_NAME_LEN);
 	if (!psk_local || !wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt))
 		wpa_ft_store_pmk_r0(sm->wpa_auth, sm->addr, pmk_r0,
-				    pmk_r0_name, sm->pairwise, vlan,
-				    expiresIn);
+				    pmk_r0_name, sm->pairwise, vlan, expiresIn,
+				    identity, identity_len, radius_cui,
+				    radius_cui_len);
 
 	wpa_derive_pmk_r1(pmk_r0, pmk_r0_name, r1kh, sm->addr,
 			  pmk_r1, sm->pmk_r1_name);
@@ -706,7 +813,8 @@  int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk,
 	if (!psk_local || !wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt))
 		wpa_ft_store_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1,
 				    sm->pmk_r1_name, sm->pairwise, vlan,
-				    expiresIn);
+				    expiresIn, identity, identity_len,
+				    radius_cui, radius_cui_len);
 
 	return wpa_pmk_r1_to_ptk(pmk_r1, sm->SNonce, sm->ANonce, sm->addr,
 				 sm->wpa_auth->addr, sm->pmk_r1_name,
@@ -1113,7 +1221,9 @@  void wpa_ft_install_ptk(struct wpa_state_machine *sm)
 static int wpa_ft_psk_pmk_r1(struct wpa_state_machine *sm,
 			     const u8 *req_pmk_r1_name,
 			     u8 *out_pmk_r1, int *out_pairwise,
-			     struct ft_vlan *out_vlan)
+			     struct ft_vlan *out_vlan, u8 *out_identity,
+			     u8 *out_identity_len, u8 *out_radius_cui,
+			     u8 *out_radius_cui_len)
 {
 	const u8 *pmk = NULL;
 	u8 pmk_r0[PMK_LEN], pmk_r0_name[WPA_PMK_NAME_LEN];
@@ -1126,6 +1236,7 @@  static int wpa_ft_psk_pmk_r1(struct wpa_state_machine *sm,
 	const u8 *ssid = wpa_auth->conf.ssid;
 	size_t ssid_len = wpa_auth->conf.ssid_len;
 	int pairwise;
+	u8 len;
 
 	pairwise = sm->pairwise;
 
@@ -1156,6 +1267,18 @@  static int wpa_ft_psk_pmk_r1(struct wpa_state_machine *sm,
 					      MACSTR, MAC2STR(sm->addr));
 			return -1;
 		}
+		if (out_identity && out_identity_len) {
+			len = wpa_ft_get_identity(sm->wpa_auth, sm->addr,
+						  out_identity,
+						  FT_IDENTITY_LEN);
+			*out_identity_len = len;
+		}
+		if (out_radius_cui && out_radius_cui_len) {
+			len = wpa_ft_get_radius_cui(sm->wpa_auth, sm->addr,
+						    out_radius_cui,
+						    FT_RADIUS_CUI_LEN);
+			*out_radius_cui_len = len;
+		}
 		return 0;
 	}
 
@@ -1218,6 +1341,8 @@  static int wpa_ft_process_auth_req(struct wpa_state_machine *sm,
 	u8 *pos, *end;
 	int pairwise;
 	struct ft_vlan vlan;
+	u8 identity[FT_IDENTITY_LEN], radius_cui[FT_RADIUS_CUI_LEN];
+	u8 identity_len = 0, radius_cui_len = 0;
 
 	*resp_ies = NULL;
 	*resp_ies_len = 0;
@@ -1279,11 +1404,13 @@  static int wpa_ft_process_auth_req(struct wpa_state_machine *sm,
 	if (conf->ft_psk_generate_local &&
 	    wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt)) {
 		if (wpa_ft_psk_pmk_r1(sm, pmk_r1_name, pmk_r1, &pairwise,
-				       &vlan) < 0)
+				      &vlan, identity, &identity_len,
+				      radius_cui, &radius_cui_len) < 0)
 			return WLAN_STATUS_INVALID_PMKID;
 	} else
 	if (wpa_ft_fetch_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1_name, pmk_r1,
-		    &pairwise, &vlan) < 0) {
+		    &pairwise, &vlan, identity, &identity_len, radius_cui,
+		    &radius_cui_len) < 0) {
 		if (wpa_ft_pull_pmk_r1(sm, ies, ies_len, parse.rsn_pmkid) < 0) {
 			wpa_printf(MSG_DEBUG, "FT: Did not have matching "
 				   "PMK-R1 and unknown R0KH-ID");
@@ -1328,6 +1455,9 @@  static int wpa_ft_process_auth_req(struct wpa_state_machine *sm,
 		wpa_printf(MSG_DEBUG, "FT: Failed to configure VLAN");
 		return WLAN_STATUS_UNSPECIFIED_FAILURE;
 	}
+	wpa_ft_set_identity(sm->wpa_auth, sm->addr, identity, identity_len);
+	wpa_ft_set_radius_cui(sm->wpa_auth, sm->addr, radius_cui,
+			      radius_cui_len);
 
 	buflen = 2 + sizeof(struct rsn_mdie) + 2 + sizeof(struct rsn_ftie) +
 		2 + FT_R1KH_ID_LEN + 200;
@@ -1792,7 +1922,6 @@  static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth,
 	struct ft_r0kh_r1kh_resp_frame resp, r;
 	u8 pmk_r0[PMK_LEN];
 	int pairwise;
-	struct ft_vlan vlan;
 	int expiresIn;
 
 	wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 pull");
@@ -1863,7 +1992,9 @@  static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth,
 	os_memcpy(r.r1kh_id, f.r1kh_id, FT_R1KH_ID_LEN);
 	os_memcpy(r.s1kh_id, f.s1kh_id, ETH_ALEN);
 	if (wpa_ft_fetch_pmk_r0(wpa_auth, f.s1kh_id, f.pmk_r0_name, pmk_r0,
-				&pairwise, &vlan, &expiresIn) < 0) {
+				&pairwise, &r.vlan, &expiresIn,
+				r.identity, &r.identity_len,
+				r.radius_cui, &r.radius_cui_len) < 0) {
 		wpa_printf(MSG_DEBUG, "FT: No matching PMKR0Name found for "
 			   "PMK-R1 pull");
 		r.expiresIn = 0xffff;
@@ -1873,8 +2004,14 @@  static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth,
 		wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", r.pmk_r1, PMK_LEN);
 		wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", r.pmk_r1_name,
 			    WPA_PMK_NAME_LEN);
+		wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 - Identity",
+				r.identity, r.identity_len);
+		wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 - Cui",
+				r.radius_cui, r.radius_cui_len);
+		wpa_printf(MSG_DEBUG, "FT: PMK-R1 - vlan %d%s",
+			   le_to_host16(r.vlan.untagged),
+			   r.vlan.tagged[0] ? "+" : "");
 		r.pairwise = host_to_le16(pairwise);
-		os_memcpy(&r.vlan, &vlan, FT_VLAN_DATA_LEN);
 		r.expiresIn = host_to_le16(expiresIn);
 	}
 	os_memset(r.pad, 0, sizeof(r.pad));
@@ -2045,6 +2182,10 @@  static int wpa_ft_rrb_rx_resp(struct wpa_authenticator *wpa_auth,
 				f.pmk_r1, PMK_LEN);
 		wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - PMKR1Name",
 				f.pmk_r1_name, WPA_PMK_NAME_LEN);
+		wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - Identity",
+				f.identity, f.identity_len);
+		wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - Cui",
+				f.radius_cui, f.radius_cui_len);
 		wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull - vlan %d%s",
 			   le_to_host16(f.vlan.untagged),
 			   f.vlan.tagged[0] ? "+" : "");
@@ -2053,7 +2194,9 @@  static int wpa_ft_rrb_rx_resp(struct wpa_authenticator *wpa_auth,
 			expiresIn = maxExpiresIn;
 		res = wpa_ft_store_pmk_r1(wpa_auth, f.s1kh_id, f.pmk_r1,
 					  f.pmk_r1_name, pairwise, f.vlan,
-					  expiresIn);
+					  expiresIn,
+					  f.identity, f.identity_len,
+					  f.radius_cui, f.radius_cui_len);
 	}
 
 	wpa_printf(MSG_DEBUG, "FT: Look for pending pull request");
@@ -2151,6 +2294,10 @@  static int wpa_ft_rrb_rx_push(struct wpa_authenticator *wpa_auth,
 			f.pmk_r1, PMK_LEN);
 	wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 push - PMKR1Name",
 			f.pmk_r1_name, WPA_PMK_NAME_LEN);
+	wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 push - Identity",
+			f.identity, f.identity_len);
+	wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 push - Cui",
+			f.radius_cui, f.radius_cui_len);
 	wpa_printf(MSG_DEBUG, "FT: PMK-R1 push - vlan %d%s",
 		   le_to_host16(f.vlan.untagged),
 		   f.vlan.tagged[0] ? "+" : "");
@@ -2158,7 +2305,8 @@  static int wpa_ft_rrb_rx_push(struct wpa_authenticator *wpa_auth,
 	if (expiresIn <= 0 || expiresIn > maxExpiresIn)
 		expiresIn = maxExpiresIn;
 	wpa_ft_store_pmk_r1(wpa_auth, f.s1kh_id, f.pmk_r1, f.pmk_r1_name,
-			    pairwise, f.vlan, expiresIn);
+			    pairwise, f.vlan, expiresIn, f.identity,
+			    f.identity_len, f.radius_cui, f.radius_cui_len);
 	os_memset(f.pmk_r1, 0, PMK_LEN);
 
 	return 0;
@@ -2323,6 +2471,10 @@  static void wpa_ft_generate_pmk_r1(struct wpa_authenticator *wpa_auth,
 	f.pairwise = host_to_le16(pairwise);
 	f.vlan = pmk_r0->vlan;
 	f.expiresIn = host_to_le16(expiresIn);
+	os_memcpy(f.identity, pmk_r0->identity, pmk_r0->identity_len);
+	f.identity_len = pmk_r0->identity_len;
+	os_memcpy(f.radius_cui, pmk_r0->radius_cui, pmk_r0->radius_cui_len);
+	f.radius_cui_len = pmk_r0->radius_cui_len;
 	os_memset(f.pad, 0, sizeof(f.pad));
 	plain = ((const u8 *) &f) + offsetof(struct ft_r0kh_r1kh_push_frame,
 					     timestamp);
diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
index 69aba7b..148367c 100644
--- a/src/ap/wpa_auth_glue.c
+++ b/src/ap/wpa_auth_glue.c
@@ -765,6 +765,152 @@  hostapd_wpa_auth_get_vlan(void *ctx, const u8 *sta_addr,
 }
 
 
+static size_t
+hostapd_wpa_auth_get_identity(void *ctx, const u8 *sta_addr, u8 *buf,
+			       size_t buflen)
+{
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta;
+	size_t len;
+	u8 *b;
+
+	sta = ap_get_sta(hapd, sta_addr);
+	if (sta == NULL)
+		return 0;
+
+	b = ieee802_1x_get_identity(sta->eapol_sm, &len);
+	if (b && len) {
+		if (len >= buflen)
+			len = buflen - 1;
+		os_memcpy(buf, b, len);
+		buf[len] = 0;
+		return len;
+	}
+
+	if (!sta->identity)
+		return 0;
+
+	len = os_strlen(sta->identity);
+	if (len >= buflen)
+		len = buflen - 1;
+	os_memcpy(buf, sta->identity, len);
+	buf[len] = 0;
+
+	return len;
+}
+
+
+static size_t
+hostapd_wpa_auth_get_radius_cui(void *ctx, const u8 *sta_addr, u8 *buf,
+				 size_t buflen)
+{
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta;
+	struct wpabuf *b;
+	size_t len;
+
+	sta = ap_get_sta(hapd, sta_addr);
+	if (sta == NULL)
+		return 0;
+
+	b = ieee802_1x_get_radius_cui(sta->eapol_sm);
+	if (b) {
+		len = wpabuf_len(b);
+		if (len >= buflen)
+			len = buflen - 1;
+		os_memcpy(buf, wpabuf_head(b), len);
+		buf[len] = 0;
+		return len;
+	}
+
+	if (!sta->radius_cui)
+		return 0;
+
+	len = os_strlen(sta->radius_cui);
+	if (len >= buflen)
+		len = buflen - 1;
+	os_memcpy(buf, sta->radius_cui, len);
+	buf[len] = 0;
+
+	return len;
+}
+
+
+static void
+hostapd_wpa_auth_set_identity(void *ctx, const u8 *sta_addr, u8 *identity,
+			       size_t identity_len)
+{
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta;
+
+	sta = ap_get_sta(hapd, sta_addr);
+	if (sta == NULL)
+		return;
+
+	if (sta->identity) {
+		os_free(sta->identity);
+		sta->identity = NULL;
+	}
+
+	if (sta->eapol_sm && sta->eapol_sm->identity) {
+		os_free(sta->eapol_sm->identity);
+		sta->eapol_sm->identity_len = 0;
+	}
+
+	if (!identity_len)
+		return;
+
+	/* sta->identity is NULL terminated */
+	sta->identity = os_zalloc(identity_len + 1);
+	if (sta->identity)
+		os_memcpy(sta->identity, identity, identity_len);
+
+	if (sta->eapol_sm) {
+		sta->eapol_sm->identity = os_zalloc(identity_len);
+		if (sta->eapol_sm->identity) {
+			os_memcpy(sta->eapol_sm->identity, identity,
+				  identity_len);
+			sta->eapol_sm->identity_len = identity_len;
+		}
+	}
+}
+
+
+static void
+hostapd_wpa_auth_set_radius_cui(void *ctx, const u8 *sta_addr, u8 *radius_cui,
+				 size_t radius_cui_len)
+{
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta;
+
+	sta = ap_get_sta(hapd, sta_addr);
+	if (sta == NULL)
+		return;
+
+	if (sta->radius_cui) {
+		os_free(sta->radius_cui);
+		sta->radius_cui = NULL;
+	}
+
+	if (sta->eapol_sm && sta->eapol_sm->radius_cui) {
+		wpabuf_free(sta->eapol_sm->radius_cui);
+		sta->eapol_sm->radius_cui = NULL;
+	}
+
+	if (!radius_cui)
+		return;
+
+	/* sta->radius_cui is NULL terminated */
+	sta->radius_cui = os_zalloc(radius_cui_len + 1);
+	if (sta->radius_cui)
+		os_memcpy(sta->radius_cui, radius_cui, radius_cui_len);
+
+	if (sta->eapol_sm)
+		sta->eapol_sm->radius_cui = wpabuf_alloc_copy(radius_cui,
+							      radius_cui_len);
+}
+
+
 static void hostapd_rrb_receive(void *ctx, const u8 *src_addr, const u8 *buf,
 				size_t len)
 {
@@ -832,6 +978,10 @@  int hostapd_setup_wpa(struct hostapd_data *hapd)
 	cb.add_sta = hostapd_wpa_auth_add_sta_auth;
 	cb.set_vlan = hostapd_wpa_auth_set_vlan;
 	cb.get_vlan = hostapd_wpa_auth_get_vlan;
+	cb.get_identity = hostapd_wpa_auth_get_identity;
+	cb.get_radius_cui = hostapd_wpa_auth_get_radius_cui;
+	cb.set_identity = hostapd_wpa_auth_set_identity;
+	cb.set_radius_cui = hostapd_wpa_auth_set_radius_cui;
 	cb.add_tspec = hostapd_wpa_auth_add_tspec;
 #endif /* CONFIG_IEEE80211R */
 	hapd->wpa_auth = wpa_init(hapd->own_addr, &_conf, &cb);
diff --git a/tests/hwsim/test_ap_ft.py b/tests/hwsim/test_ap_ft.py
index 9657ea4..ef78cce 100644
--- a/tests/hwsim/test_ap_ft.py
+++ b/tests/hwsim/test_ap_ft.py
@@ -608,12 +608,15 @@  def test_ap_ft_sae_over_ds(dev, apdev):
     run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, sae=True,
               over_ds=True)
 
-def generic_ap_ft_eap(dev, apdev, vlan, over_ds=False, discovery=False, roams=1):
+def generic_ap_ft_eap(dev, apdev, vlan, cui=False, over_ds=False, discovery=False, roams=1):
     ssid = "test-ft"
     passphrase="12345678"
     if vlan:
         identity="gpsk-vlan1"
         conndev="brvlan1"
+    elif cui:
+        identity="gpsk-cui"
+        conndev=False
     else:
         identity="gpsk user"
         conndev=False
@@ -665,6 +668,10 @@  def test_ap_ft_eap(dev, apdev):
     """WPA2-EAP-FT AP"""
     generic_ap_ft_eap(dev, apdev, vlan=False)
 
+def test_ap_ft_eap_cui(dev, apdev):
+    """WPA2-EAP-FT AP with cui"""
+    generic_ap_ft_eap(dev, apdev, vlan=False, cui=True)
+
 def test_ap_ft_eap_vlan(dev, apdev):
     """WPA2-EAP-FT AP w VLAN"""
     generic_ap_ft_eap(dev, apdev, vlan=True)