FT: Fix FT 4-Way Handshake to include PMKR1Name in messages 2 and 3

IEEE Std 802.11r-2008, 11A.4.2 describes FT initial mobility domain
association in an RSN to include PMKR1Name in the PMKID-List field
in RSN IE in messages 2/4 and 3/4. This makes the RSN IE not be
bitwise identical with the values used in Beacon, Probe Response,
(Re)association Request frames.

The previous versions of wpa_supplicant and hostapd did not add the
PMKR1Name value in EAPOL-Key frame and did not accept it if added
(due to bitwise comparison of RSN IEs). This commit fixes the
implementation to be compliant with the standard by adding the
PMKR1Name value into EAPOL-Key messages during FT 4-Way Handshake and
by verifying that the received value matches with the value derived
locally.

This breaks interoperability with previous wpa_supplicant/hostapd
versions.
This commit is contained in:
Jouni Malinen 2010-04-07 21:04:13 +03:00
parent 738a1cb286
commit 26e23750b9
7 changed files with 247 additions and 14 deletions

View file

@ -609,6 +609,7 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
SMK_M1, SMK_M3, SMK_ERROR } msg;
char *msgtxt;
struct wpa_eapol_ie_parse kde;
int ft;
if (wpa_auth == NULL || !wpa_auth->conf.wpa || sm == NULL)
return;
@ -739,9 +740,12 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
sm->wpa_ptk_state);
return;
}
ft = sm->wpa == WPA_VERSION_WPA2 &&
wpa_key_mgmt_ft(sm->wpa_key_mgmt);
if (sm->wpa_ie == NULL ||
sm->wpa_ie_len != key_data_length ||
os_memcmp(sm->wpa_ie, key + 1, key_data_length) != 0) {
wpa_compare_rsn_ie(ft,
sm->wpa_ie, sm->wpa_ie_len,
(u8 *) (key + 1), key_data_length)) {
wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
"WPA IE from (Re)AssocReq did not "
"match with msg 2/4");
@ -755,6 +759,22 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
wpa_sta_disconnect(wpa_auth, sm->addr);
return;
}
#ifdef CONFIG_IEEE80211R
if (ft) {
struct wpa_ie_data ie;
if (wpa_parse_wpa_ie_rsn((u8 *) (key + 1),
key_data_length, &ie) < 0 ||
ie.num_pmkid != 1 || ie.pmkid == NULL) {
wpa_printf(MSG_DEBUG, "FT: No PMKR1Name in "
"FT 4-way handshake message 2/4");
wpa_sta_disconnect(wpa_auth, sm->addr);
return;
}
os_memcpy(sm->sup_pmk_r1_name, ie.pmkid, PMKID_LEN);
wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name from Supplicant",
sm->sup_pmk_r1_name, PMKID_LEN);
}
#endif /* CONFIG_IEEE80211R */
break;
case PAIRWISE_4:
if (sm->wpa_ptk_state != WPA_PTK_PTKINITNEGOTIATING ||
@ -1487,6 +1507,27 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
return;
}
#ifdef CONFIG_IEEE80211R
if (sm->wpa == WPA_VERSION_WPA2 && wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
/*
* Verify that PMKR1Name from EAPOL-Key message 2/4 matches
* with the value we derived.
*/
if (os_memcmp(sm->sup_pmk_r1_name, sm->pmk_r1_name,
WPA_PMK_NAME_LEN) != 0) {
wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
"PMKR1Name mismatch in FT 4-way "
"handshake");
wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name from "
"Supplicant",
sm->sup_pmk_r1_name, WPA_PMK_NAME_LEN);
wpa_hexdump(MSG_DEBUG, "FT: Derived PMKR1Name",
sm->pmk_r1_name, WPA_PMK_NAME_LEN);
return;
}
}
#endif /* CONFIG_IEEE80211R */
eloop_cancel_timeout(wpa_send_eapol_timeout, sm->wpa_auth, sm);
if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) {
@ -1611,6 +1652,10 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
kde_len = wpa_ie_len + ieee80211w_kde_len(sm);
if (gtk)
kde_len += 2 + RSN_SELECTOR_LEN + 2 + gtk_len;
#ifdef CONFIG_IEEE80211R
if (wpa_key_mgmt_ft(sm->wpa_key_mgmt))
kde_len += 2 + PMKID_LEN;
#endif /* CONFIG_IEEE80211R */
kde = os_malloc(kde_len);
if (kde == NULL)
return;
@ -1618,6 +1663,18 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
pos = kde;
os_memcpy(pos, wpa_ie, wpa_ie_len);
pos += wpa_ie_len;
#ifdef CONFIG_IEEE80211R
if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
int res = wpa_insert_pmkid(kde, pos - kde, sm->pmk_r1_name);
if (res < 0) {
wpa_printf(MSG_ERROR, "FT: Failed to insert "
"PMKR1Name into RSN IE in EAPOL-Key data");
os_free(kde);
return;
}
pos += res;
}
#endif /* CONFIG_IEEE80211R */
if (gtk) {
u8 hdr[2];
hdr[0] = keyidx & 0x03;

View file

@ -356,7 +356,7 @@ int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk,
struct wpa_ptk *ptk, size_t ptk_len)
{
u8 pmk_r0[PMK_LEN], pmk_r0_name[WPA_PMK_NAME_LEN];
u8 pmk_r1[PMK_LEN], pmk_r1_name[WPA_PMK_NAME_LEN];
u8 pmk_r1[PMK_LEN];
u8 ptk_name[WPA_PMK_NAME_LEN];
const u8 *mdid = sm->wpa_auth->conf.mobility_domain;
const u8 *r0kh = sm->wpa_auth->conf.r0_key_holder;
@ -380,14 +380,15 @@ int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk,
sm->pairwise);
wpa_derive_pmk_r1(pmk_r0, pmk_r0_name, r1kh, sm->addr,
pmk_r1, pmk_r1_name);
pmk_r1, sm->pmk_r1_name);
wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", pmk_r1, PMK_LEN);
wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", pmk_r1_name, WPA_PMK_NAME_LEN);
wpa_ft_store_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1, pmk_r1_name,
wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", sm->pmk_r1_name,
WPA_PMK_NAME_LEN);
wpa_ft_store_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1, sm->pmk_r1_name,
sm->pairwise);
wpa_pmk_r1_to_ptk(pmk_r1, sm->SNonce, sm->ANonce, sm->addr,
sm->wpa_auth->addr, pmk_r1_name,
sm->wpa_auth->addr, sm->pmk_r1_name,
(u8 *) ptk, ptk_len, ptk_name);
wpa_hexdump_key(MSG_DEBUG, "FT: PTK", (u8 *) ptk, ptk_len);
wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN);

View file

@ -116,6 +116,8 @@ struct wpa_state_machine {
* Request */
u8 r0kh_id[FT_R0KH_ID_MAX_LEN]; /* R0KH-ID from FT Auth Request */
size_t r0kh_id_len;
u8 sup_pmk_r1_name[WPA_PMK_NAME_LEN]; /* PMKR1Name from EAPOL-Key
* message 2/4 */
#endif /* CONFIG_IEEE80211R */
};