PASN: Support PASN with FT key derivation
Add support for PASN authentication with FT key derivation: - As IEEE P802.11az/D2.6 states that wrapped data is optional and is only needed for further validation of the FT security parameters, do not include them in the first PASN frame. - PASN with FT key derivation requires knowledge of the PMK-R1 and PMK-R1-Name for the target AP. As the WPA state machine stores PMK-R1, etc. only for the currently associated AP, store the mapping of BSSID to R1KH-ID for each previous association, so the R1KH-ID could be used to derive PMK-R1 and PMK-R1-Name. Do so instead of storing the PMK-R1 to avoid maintaining keys that might not be used. Signed-off-by: Ilan Peer <ilan.peer@intel.com>
This commit is contained in:
parent
09a9e9939c
commit
5c65ad6c0b
6 changed files with 194 additions and 7 deletions
|
@ -3819,6 +3819,11 @@ void wpa_sm_drop_sa(struct wpa_sm *sm)
|
||||||
sm->pmk_r0_len = 0;
|
sm->pmk_r0_len = 0;
|
||||||
os_memset(sm->pmk_r1, 0, sizeof(sm->pmk_r1));
|
os_memset(sm->pmk_r1, 0, sizeof(sm->pmk_r1));
|
||||||
sm->pmk_r1_len = 0;
|
sm->pmk_r1_len = 0;
|
||||||
|
#ifdef CONFIG_PASN
|
||||||
|
os_free(sm->pasn_r1kh);
|
||||||
|
sm->pasn_r1kh = NULL;
|
||||||
|
sm->n_pasn_r1kh = 0;
|
||||||
|
#endif /* CONFIG_PASN */
|
||||||
#endif /* CONFIG_IEEE80211R */
|
#endif /* CONFIG_IEEE80211R */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -430,6 +430,13 @@ int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies,
|
||||||
int wpa_ft_start_over_ds(struct wpa_sm *sm, const u8 *target_ap,
|
int wpa_ft_start_over_ds(struct wpa_sm *sm, const u8 *target_ap,
|
||||||
const u8 *mdie);
|
const u8 *mdie);
|
||||||
|
|
||||||
|
#ifdef CONFIG_PASN
|
||||||
|
|
||||||
|
int wpa_pasn_ft_derive_pmk_r1(struct wpa_sm *sm, int akmp, const u8 *r1kh_id,
|
||||||
|
u8 *pmk_r1, size_t *pmk_r1_len, u8 *pmk_r1_name);
|
||||||
|
|
||||||
|
#endif /* CONFIG_PASN */
|
||||||
|
|
||||||
#else /* CONFIG_IEEE80211R */
|
#else /* CONFIG_IEEE80211R */
|
||||||
|
|
||||||
static inline int
|
static inline int
|
||||||
|
@ -473,6 +480,16 @@ wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, size_t ies_len,
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_PASN
|
||||||
|
|
||||||
|
int wpa_pasn_ft_derive_pmk_r1(struct wpa_sm *sm, int akmp, const u8 *r1kh_id,
|
||||||
|
u8 *pmk_r1, size_t *pmk_r1_len, u8 *pmk_r1_name)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* CONFIG_PASN */
|
||||||
|
|
||||||
#endif /* CONFIG_IEEE80211R */
|
#endif /* CONFIG_IEEE80211R */
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,15 @@
|
||||||
|
|
||||||
#ifdef CONFIG_IEEE80211R
|
#ifdef CONFIG_IEEE80211R
|
||||||
|
|
||||||
|
#ifdef CONFIG_PASN
|
||||||
|
static void wpa_ft_pasn_store_r1kh(struct wpa_sm *sm, const u8 *bssid);
|
||||||
|
#else /* CONFIG_PASN */
|
||||||
|
static void wpa_ft_pasn_store_r1kh(struct wpa_sm *sm, const u8 *bssid)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_PASN */
|
||||||
|
|
||||||
|
|
||||||
int wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr,
|
int wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr,
|
||||||
const struct wpa_eapol_key *key, struct wpa_ptk *ptk)
|
const struct wpa_eapol_key *key, struct wpa_ptk *ptk)
|
||||||
{
|
{
|
||||||
|
@ -56,6 +65,9 @@ int wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr,
|
||||||
sm->r1kh_id, sm->own_addr, sm->pmk_r1,
|
sm->r1kh_id, sm->own_addr, sm->pmk_r1,
|
||||||
sm->pmk_r1_name) < 0)
|
sm->pmk_r1_name) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
wpa_ft_pasn_store_r1kh(sm, src_addr);
|
||||||
|
|
||||||
return wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->pmk_r1_len, sm->snonce, anonce,
|
return wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->pmk_r1_len, sm->snonce, anonce,
|
||||||
sm->own_addr, sm->bssid, sm->pmk_r1_name, ptk,
|
sm->own_addr, sm->bssid, sm->pmk_r1_name, ptk,
|
||||||
ptk_name, sm->key_mgmt, sm->pairwise_cipher,
|
ptk_name, sm->key_mgmt, sm->pairwise_cipher,
|
||||||
|
@ -649,6 +661,9 @@ int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len,
|
||||||
sm->pmk_r1_len = sm->pmk_r0_len;
|
sm->pmk_r1_len = sm->pmk_r0_len;
|
||||||
|
|
||||||
bssid = target_ap;
|
bssid = target_ap;
|
||||||
|
|
||||||
|
wpa_ft_pasn_store_r1kh(sm, bssid);
|
||||||
|
|
||||||
if (wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->pmk_r1_len, sm->snonce,
|
if (wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->pmk_r1_len, sm->snonce,
|
||||||
anonce, sm->own_addr, bssid,
|
anonce, sm->own_addr, bssid,
|
||||||
sm->pmk_r1_name, &sm->ptk, ptk_name, sm->key_mgmt,
|
sm->pmk_r1_name, &sm->ptk, ptk_name, sm->key_mgmt,
|
||||||
|
@ -1242,4 +1257,88 @@ int wpa_ft_start_over_ds(struct wpa_sm *sm, const u8 *target_ap,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef CONFIG_PASN
|
||||||
|
|
||||||
|
static struct pasn_ft_r1kh * wpa_ft_pasn_get_r1kh(struct wpa_sm *sm,
|
||||||
|
const u8 *bssid)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < sm->n_pasn_r1kh; i++)
|
||||||
|
if (os_memcmp(sm->pasn_r1kh[i].bssid, bssid, ETH_ALEN) == 0)
|
||||||
|
return &sm->pasn_r1kh[i];
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void wpa_ft_pasn_store_r1kh(struct wpa_sm *sm, const u8 *bssid)
|
||||||
|
{
|
||||||
|
struct pasn_ft_r1kh *tmp = wpa_ft_pasn_get_r1kh(sm, bssid);
|
||||||
|
|
||||||
|
if (tmp)
|
||||||
|
return;
|
||||||
|
|
||||||
|
tmp = os_realloc_array(sm->pasn_r1kh, sm->n_pasn_r1kh + 1,
|
||||||
|
sizeof(*tmp));
|
||||||
|
if (!tmp) {
|
||||||
|
wpa_printf(MSG_DEBUG, "PASN: FT: Failed to store R1KH");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sm->pasn_r1kh = tmp;
|
||||||
|
tmp = &sm->pasn_r1kh[sm->n_pasn_r1kh];
|
||||||
|
|
||||||
|
wpa_printf(MSG_DEBUG, "PASN: FT: Store R1KH for " MACSTR,
|
||||||
|
MAC2STR(bssid));
|
||||||
|
|
||||||
|
os_memcpy(tmp->bssid, bssid, ETH_ALEN);
|
||||||
|
os_memcpy(tmp->r1kh_id, sm->r1kh_id, FT_R1KH_ID_LEN);
|
||||||
|
|
||||||
|
sm->n_pasn_r1kh++;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int wpa_pasn_ft_derive_pmk_r1(struct wpa_sm *sm, int akmp, const u8 *bssid,
|
||||||
|
u8 *pmk_r1, size_t *pmk_r1_len, u8 *pmk_r1_name)
|
||||||
|
{
|
||||||
|
struct pasn_ft_r1kh *r1kh_entry;
|
||||||
|
|
||||||
|
if (sm->key_mgmt != (unsigned int) akmp) {
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"PASN: FT: Key management mismatch: %u != %u",
|
||||||
|
sm->key_mgmt, akmp);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
r1kh_entry = wpa_ft_pasn_get_r1kh(sm, bssid);
|
||||||
|
if (!r1kh_entry) {
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"PASN: FT: Cannot find R1KH-ID for " MACSTR,
|
||||||
|
MAC2STR(bssid));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Note: PMK R0 etc. were already derived and are maintained by the
|
||||||
|
* state machine, and as the same key hierarchy is used, there is no
|
||||||
|
* need to derive them again, so only derive PMK R1 etc.
|
||||||
|
*/
|
||||||
|
if (wpa_derive_pmk_r1(sm->pmk_r0, sm->pmk_r0_len, sm->pmk_r0_name,
|
||||||
|
r1kh_entry->r1kh_id, sm->own_addr, pmk_r1,
|
||||||
|
pmk_r1_name) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
*pmk_r1_len = sm->pmk_r0_len;
|
||||||
|
|
||||||
|
wpa_hexdump_key(MSG_DEBUG, "PASN: FT: PMK-R1", pmk_r1, sm->pmk_r0_len);
|
||||||
|
wpa_hexdump(MSG_DEBUG, "PASN: FT: PMKR1Name", pmk_r1_name,
|
||||||
|
WPA_PMK_NAME_LEN);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* CONFIG_PASN */
|
||||||
|
|
||||||
#endif /* CONFIG_IEEE80211R */
|
#endif /* CONFIG_IEEE80211R */
|
||||||
|
|
|
@ -14,6 +14,11 @@
|
||||||
struct wpa_tdls_peer;
|
struct wpa_tdls_peer;
|
||||||
struct wpa_eapol_key;
|
struct wpa_eapol_key;
|
||||||
|
|
||||||
|
struct pasn_ft_r1kh {
|
||||||
|
u8 bssid[ETH_ALEN];
|
||||||
|
u8 r1kh_id[FT_R1KH_ID_LEN];
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct wpa_sm - Internal WPA state machine data
|
* struct wpa_sm - Internal WPA state machine data
|
||||||
*/
|
*/
|
||||||
|
@ -152,6 +157,17 @@ struct wpa_sm {
|
||||||
u8 mdie_ft_capab; /* FT Capability and Policy from target AP MDIE */
|
u8 mdie_ft_capab; /* FT Capability and Policy from target AP MDIE */
|
||||||
u8 *assoc_resp_ies; /* MDIE and FTIE from (Re)Association Response */
|
u8 *assoc_resp_ies; /* MDIE and FTIE from (Re)Association Response */
|
||||||
size_t assoc_resp_ies_len;
|
size_t assoc_resp_ies_len;
|
||||||
|
#ifdef CONFIG_PASN
|
||||||
|
/*
|
||||||
|
* Currently, the WPA state machine stores the PMK-R1, PMK-R1-Name and
|
||||||
|
* R1KH-ID only for the current association. As PMK-R1 is required to
|
||||||
|
* perform PASN authentication with FT, store the R1KH-ID for previous
|
||||||
|
* associations, which would later be used to derive the PMK-R1 as part
|
||||||
|
* of the PASN authentication flow.
|
||||||
|
*/
|
||||||
|
struct pasn_ft_r1kh *pasn_r1kh;
|
||||||
|
unsigned int n_pasn_r1kh;
|
||||||
|
#endif /* CONFIG_PASN */
|
||||||
#endif /* CONFIG_IEEE80211R */
|
#endif /* CONFIG_IEEE80211R */
|
||||||
|
|
||||||
#ifdef CONFIG_P2P
|
#ifdef CONFIG_P2P
|
||||||
|
|
|
@ -569,6 +569,12 @@ static struct wpabuf * wpas_pasn_get_wrapped_data(struct wpa_supplicant *wpa_s)
|
||||||
case WPA_KEY_MGMT_FT_PSK:
|
case WPA_KEY_MGMT_FT_PSK:
|
||||||
case WPA_KEY_MGMT_FT_IEEE8021X:
|
case WPA_KEY_MGMT_FT_IEEE8021X:
|
||||||
case WPA_KEY_MGMT_FT_IEEE8021X_SHA384:
|
case WPA_KEY_MGMT_FT_IEEE8021X_SHA384:
|
||||||
|
/*
|
||||||
|
* Wrapped data with these AKMs is optional and is only needed
|
||||||
|
* for further validation of FT security parameters. For now do
|
||||||
|
* not use them.
|
||||||
|
*/
|
||||||
|
return NULL;
|
||||||
default:
|
default:
|
||||||
wpa_printf(MSG_ERROR,
|
wpa_printf(MSG_ERROR,
|
||||||
"PASN: TODO: Wrapped data for akmp=0x%x",
|
"PASN: TODO: Wrapped data for akmp=0x%x",
|
||||||
|
@ -593,7 +599,12 @@ static u8 wpas_pasn_get_wrapped_data_format(struct wpas_pasn *pasn)
|
||||||
case WPA_KEY_MGMT_FT_PSK:
|
case WPA_KEY_MGMT_FT_PSK:
|
||||||
case WPA_KEY_MGMT_FT_IEEE8021X:
|
case WPA_KEY_MGMT_FT_IEEE8021X:
|
||||||
case WPA_KEY_MGMT_FT_IEEE8021X_SHA384:
|
case WPA_KEY_MGMT_FT_IEEE8021X_SHA384:
|
||||||
return WPA_PASN_WRAPPED_DATA_FT;
|
/*
|
||||||
|
* Wrapped data with these AKMs is optional and is only needed
|
||||||
|
* for further validation of FT security parameters. For now do
|
||||||
|
* not use them.
|
||||||
|
*/
|
||||||
|
return WPA_PASN_WRAPPED_DATA_NO;
|
||||||
case WPA_KEY_MGMT_PASN:
|
case WPA_KEY_MGMT_PASN:
|
||||||
default:
|
default:
|
||||||
return WPA_PASN_WRAPPED_DATA_NO;
|
return WPA_PASN_WRAPPED_DATA_NO;
|
||||||
|
@ -605,7 +616,7 @@ static struct wpabuf * wpas_pasn_build_auth_1(struct wpa_supplicant *wpa_s)
|
||||||
{
|
{
|
||||||
struct wpas_pasn *pasn = &wpa_s->pasn;
|
struct wpas_pasn *pasn = &wpa_s->pasn;
|
||||||
struct wpabuf *buf, *pubkey = NULL, *wrapped_data_buf = NULL;
|
struct wpabuf *buf, *pubkey = NULL, *wrapped_data_buf = NULL;
|
||||||
struct rsn_pmksa_cache_entry *pmksa;
|
const u8 *pmkid;
|
||||||
u8 wrapped_data;
|
u8 wrapped_data;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
@ -632,21 +643,36 @@ static struct wpabuf * wpas_pasn_build_auth_1(struct wpa_supplicant *wpa_s)
|
||||||
wpa_s->own_addr, pasn->bssid,
|
wpa_s->own_addr, pasn->bssid,
|
||||||
pasn->trans_seq + 1, WLAN_STATUS_SUCCESS);
|
pasn->trans_seq + 1, WLAN_STATUS_SUCCESS);
|
||||||
|
|
||||||
if (wrapped_data != WPA_PASN_WRAPPED_DATA_NO) {
|
pmkid = NULL;
|
||||||
|
if (wpa_key_mgmt_ft(pasn->akmp)) {
|
||||||
|
ret = wpa_pasn_ft_derive_pmk_r1(wpa_s->wpa, pasn->akmp,
|
||||||
|
pasn->bssid,
|
||||||
|
pasn->pmk_r1,
|
||||||
|
&pasn->pmk_r1_len,
|
||||||
|
pasn->pmk_r1_name);
|
||||||
|
if (ret) {
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"PASN: FT: Failed to derive keys");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
pmkid = pasn->pmk_r1_name;
|
||||||
|
} else if (wrapped_data != WPA_PASN_WRAPPED_DATA_NO) {
|
||||||
|
struct rsn_pmksa_cache_entry *pmksa;
|
||||||
|
|
||||||
pmksa = wpa_sm_pmksa_cache_get(wpa_s->wpa, pasn->bssid,
|
pmksa = wpa_sm_pmksa_cache_get(wpa_s->wpa, pasn->bssid,
|
||||||
NULL, NULL, pasn->akmp);
|
NULL, NULL, pasn->akmp);
|
||||||
|
if (pmksa)
|
||||||
|
pmkid = pmksa->pmkid;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Note: Even when PMKSA is available, also add wrapped data as
|
* Note: Even when PMKSA is available, also add wrapped data as
|
||||||
* it is possible that the PMKID is no longer valid at the AP.
|
* it is possible that the PMKID is no longer valid at the AP.
|
||||||
*/
|
*/
|
||||||
wrapped_data_buf = wpas_pasn_get_wrapped_data(wpa_s);
|
wrapped_data_buf = wpas_pasn_get_wrapped_data(wpa_s);
|
||||||
} else {
|
|
||||||
pmksa = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wpa_pasn_add_rsne(buf, pmksa ? pmksa->pmkid : NULL,
|
if (wpa_pasn_add_rsne(buf, pmkid, pasn->akmp, pasn->cipher) < 0)
|
||||||
pasn->akmp, pasn->cipher) < 0)
|
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
if (!wrapped_data_buf)
|
if (!wrapped_data_buf)
|
||||||
|
@ -791,6 +817,11 @@ static void wpas_pasn_reset(struct wpa_supplicant *wpa_s)
|
||||||
os_memset(&pasn->fils, 0, sizeof(pasn->fils));
|
os_memset(&pasn->fils, 0, sizeof(pasn->fils));
|
||||||
#endif /* CONFIG_FILS*/
|
#endif /* CONFIG_FILS*/
|
||||||
|
|
||||||
|
#ifdef CONFIG_IEEE80211R
|
||||||
|
forced_memzero(pasn->pmk_r1, sizeof(pasn->pmk_r1));
|
||||||
|
pasn->pmk_r1_len = 0;
|
||||||
|
os_memset(pasn->pmk_r1_name, 0, sizeof(pasn->pmk_r1_name));
|
||||||
|
#endif /* CONFIG_IEEE80211R */
|
||||||
pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
|
pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -815,6 +846,19 @@ static int wpas_pasn_set_pmk(struct wpa_supplicant *wpa_s,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (wpa_key_mgmt_ft(pasn->akmp)) {
|
||||||
|
#ifdef CONFIG_IEEE80211R
|
||||||
|
wpa_printf(MSG_DEBUG, "PASN: FT: Using PMK-R1");
|
||||||
|
pasn->pmk_len = pasn->pmk_r1_len;
|
||||||
|
os_memcpy(pasn->pmk, pasn->pmk_r1, pasn->pmk_r1_len);
|
||||||
|
pasn->using_pmksa = true;
|
||||||
|
return 0;
|
||||||
|
#else /* CONFIG_IEEE80211R */
|
||||||
|
wpa_printf(MSG_DEBUG, "PASN: FT: Not supported");
|
||||||
|
return -1;
|
||||||
|
#endif /* CONFIG_IEEE80211R */
|
||||||
|
}
|
||||||
|
|
||||||
if (rsn_data->num_pmkid) {
|
if (rsn_data->num_pmkid) {
|
||||||
struct rsn_pmksa_cache_entry *pmksa;
|
struct rsn_pmksa_cache_entry *pmksa;
|
||||||
|
|
||||||
|
|
|
@ -555,6 +555,12 @@ struct wpas_pasn {
|
||||||
#ifdef CONFIG_FILS
|
#ifdef CONFIG_FILS
|
||||||
struct pasn_fils fils;
|
struct pasn_fils fils;
|
||||||
#endif /* CONFIG_FILS */
|
#endif /* CONFIG_FILS */
|
||||||
|
|
||||||
|
#ifdef CONFIG_IEEE80211R
|
||||||
|
u8 pmk_r1[PMK_LEN_MAX];
|
||||||
|
size_t pmk_r1_len;
|
||||||
|
u8 pmk_r1_name[WPA_PMK_NAME_LEN];
|
||||||
|
#endif /* CONFIG_IEEE80211R */
|
||||||
};
|
};
|
||||||
#endif /* CONFIG_PASN */
|
#endif /* CONFIG_PASN */
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue