From: Benjamin Berg <benjamin.berg@xxxxxxxxxxxxx> This patch adds a new option to set a static key for the communication between the APs during 802.11r roaming. For this to work hostapd has to assume that both the nas_identifier and R1KH identifier match the MAC/BSSID of the hostapd instance. The advantage is that all APs can share a common configuration and roaming works throughout the network without each AP having a list of all other APs and a key for them. It is assumed that all APs can communicate on a common layer two network. Signed-off-by: Benjamin Berg <benjamin.berg@xxxxxxxxxxxxx> --- hostapd/config_file.c | 9 +++ src/ap/ap_config.h | 2 + src/ap/wpa_auth.h | 2 + src/ap/wpa_auth_ft.c | 173 +++++++++++++++++++++++++++++++++++++------------ src/ap/wpa_auth_glue.c | 2 + 5 files changed, 146 insertions(+), 42 deletions(-) diff --git a/hostapd/config_file.c b/hostapd/config_file.c index 5079f69..a43fa2f 100644 --- a/hostapd/config_file.c +++ b/hostapd/config_file.c @@ -2555,6 +2555,15 @@ static int hostapd_config_fill(struct hostapd_config *conf, line, pos); return 1; } + } else if (os_strcmp(buf, "ft_remote_key") == 0) { + + bss->ft_remote_trust_khid = 1; + + if (hexstr2bin(pos, bss->ft_remote_key, sizeof(bss->ft_remote_key))) { + wpa_printf(MSG_ERROR, "Invalid FT communication key: '%s'", pos); + return 1; + } + } else if (os_strcmp(buf, "pmk_r1_push") == 0) { bss->pmk_r1_push = atoi(pos); } else if (os_strcmp(buf, "ft_over_ds") == 0) { diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h index 8c8f7e2..5896f90 100644 --- a/src/ap/ap_config.h +++ b/src/ap/ap_config.h @@ -337,6 +337,8 @@ struct hostapd_bss_config { u32 reassociation_deadline; struct ft_remote_r0kh *r0kh_list; struct ft_remote_r1kh *r1kh_list; + int ft_remote_trust_khid; + u8 ft_remote_key[16]; int pmk_r1_push; int ft_over_ds; #endif /* CONFIG_IEEE80211R */ diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h index 0de8d97..cd70912 100644 --- a/src/ap/wpa_auth.h +++ b/src/ap/wpa_auth.h @@ -168,6 +168,8 @@ struct wpa_auth_config { u32 reassociation_deadline; struct ft_remote_r0kh *r0kh_list; struct ft_remote_r1kh *r1kh_list; + int ft_remote_trust_khid; + u8 ft_remote_key[16]; int pmk_r1_push; int ft_over_ds; #endif /* CONFIG_IEEE80211R */ diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c index 42242a5..a6e277d 100644 --- a/src/ap/wpa_auth_ft.c +++ b/src/ap/wpa_auth_ft.c @@ -300,30 +300,125 @@ static int wpa_ft_fetch_pmk_r1(struct wpa_authenticator *wpa_auth, return -1; } - -static int wpa_ft_pull_pmk_r1(struct wpa_state_machine *sm, - const u8 *ies, size_t ies_len, - const u8 *pmk_r0_name) +static int wpa_ft_r0kh_find_by_id(struct wpa_authenticator *wpa_auth, + const u8 *r0kh_id, + int r0kh_id_len, + u8 *addr, + u8 *key) { struct ft_remote_r0kh *r0kh; - struct ft_r0kh_r1kh_pull_frame frame, f; - r0kh = sm->wpa_auth->conf.r0kh_list; + /* First try to lookup in list. */ + r0kh = wpa_auth->conf.r0kh_list; while (r0kh) { - if (r0kh->id_len == sm->r0kh_id_len && - os_memcmp_const(r0kh->id, sm->r0kh_id, sm->r0kh_id_len) == + if (r0kh->id_len == r0kh_id_len && + os_memcmp_const(r0kh->id, r0kh_id, r0kh_id_len) == 0) break; r0kh = r0kh->next; } if (r0kh == NULL) { - wpa_hexdump(MSG_DEBUG, "FT: Did not find R0KH-ID", + /* If we trust the KHID to be a MAC, then use this instead. + **/ + if (wpa_auth->conf.ft_remote_trust_khid && r0kh_id_len == (3*6-1)) { + if (hwaddr_aton((char*) r0kh_id, addr)) { + return -1; + } + os_memcpy(key, wpa_auth->conf.ft_remote_key, + sizeof(wpa_auth->conf.ft_remote_key)); + } else { + return -1; + } + } else { + os_memcpy(key, r0kh->key, + sizeof(r0kh->key)); + os_memcpy(addr, r0kh->addr, + ETH_ALEN); + } + + return 0; +} + +static int wpa_ft_r0kh_find_by_addr(struct wpa_authenticator *wpa_auth, + const u8 *r0kh_addr, + u8 *key) +{ + struct ft_remote_r0kh *r0kh; + + /* First try to lookup in list. */ + r0kh = wpa_auth->conf.r0kh_list; + while (r0kh) { + if (os_memcmp_const(r0kh->addr, r0kh_addr, ETH_ALEN) == 0) + break; + r0kh = r0kh->next; + } + if (r0kh == NULL) { + /* If we trust the KHID to be a MAC, then use this instead. + **/ + if (wpa_auth->conf.ft_remote_trust_khid) { + os_memcpy(key, wpa_auth->conf.ft_remote_key, + sizeof(wpa_auth->conf.ft_remote_key)); + } else { + return -1; + } + } else { + os_memcpy(key, r0kh->key, + sizeof(r0kh->key)); + } + + return 0; +} + +static int wpa_ft_r1kh_find_by_addr(struct wpa_authenticator *wpa_auth, + const u8 *src_addr, + u8 *id, + u8 *key) +{ + struct ft_remote_r1kh *r1kh; + + /* First try to lookup in list. */ + r1kh = wpa_auth->conf.r1kh_list; + while (r1kh) { + if (os_memcmp(r1kh->addr, src_addr, ETH_ALEN) == 0) + break; + r1kh = r1kh->next; + } + if (r1kh == NULL) { + /* If we trust the KHID to be a MAC, then use this instead. + **/ + if (wpa_auth->conf.ft_remote_trust_khid) { + os_memcpy(id, src_addr, ETH_ALEN); + os_memcpy(key, wpa_auth->conf.ft_remote_key, + sizeof(wpa_auth->conf.ft_remote_key)); + } else { + return -1; + } + } else { + os_memcpy(id, r1kh->id, ETH_ALEN); + os_memcpy(key, r1kh->key, + sizeof(r1kh->key)); + } + + return 0; +} + +static int wpa_ft_pull_pmk_r1(struct wpa_state_machine *sm, + const u8 *ies, size_t ies_len, + const u8 *pmk_r0_name) +{ + macaddr r0kh_addr; + u8 r0kh_key[16]; + struct ft_r0kh_r1kh_pull_frame frame, f; + + if (wpa_ft_r0kh_find_by_id(sm->wpa_auth, sm->r0kh_id, sm->r0kh_id_len, + r0kh_addr, r0kh_key)) { + wpa_hexdump(MSG_DEBUG, "FT: Cannot pull PMK-R1 for R0KH", sm->r0kh_id, sm->r0kh_id_len); return -1; } wpa_printf(MSG_DEBUG, "FT: Send PMK-R1 pull request to remote R0KH " - "address " MACSTR, MAC2STR(r0kh->addr)); + "address " MACSTR, MAC2STR(r0kh_addr)); os_memset(&frame, 0, sizeof(frame)); frame.frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB; @@ -345,7 +440,7 @@ static int wpa_ft_pull_pmk_r1(struct wpa_state_machine *sm, os_memcpy(f.s1kh_id, sm->addr, ETH_ALEN); os_memset(f.pad, 0, sizeof(f.pad)); - if (aes_wrap(r0kh->key, sizeof(r0kh->key), + if (aes_wrap(r0kh_key, sizeof(r0kh_key), (FT_R0KH_R1KH_PULL_DATA_LEN + 7) / 8, f.nonce, frame.nonce) < 0) return -1; @@ -355,7 +450,7 @@ static int wpa_ft_pull_pmk_r1(struct wpa_state_machine *sm, if (sm->ft_pending_req_ies == NULL) return -1; - wpa_ft_rrb_send(sm->wpa_auth, r0kh->addr, (u8 *) &frame, sizeof(frame)); + wpa_ft_rrb_send(sm->wpa_auth, r0kh_addr, (u8 *) &frame, sizeof(frame)); return 0; } @@ -1313,7 +1408,8 @@ static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth, struct ft_r0kh_r1kh_pull_frame f; const u8 *crypt; u8 *plain; - struct ft_remote_r1kh *r1kh; + u8 r1kh_id[FT_R1KH_ID_LEN]; + u8 r1kh_key[16]; struct ft_r0kh_r1kh_resp_frame resp, r; u8 pmk_r0[PMK_LEN]; int pairwise; @@ -1323,13 +1419,8 @@ static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth, if (data_len < sizeof(f)) return -1; - r1kh = wpa_auth->conf.r1kh_list; - while (r1kh) { - if (os_memcmp(r1kh->addr, src_addr, ETH_ALEN) == 0) - break; - r1kh = r1kh->next; - } - if (r1kh == NULL) { + + if (wpa_ft_r1kh_find_by_addr(wpa_auth, src_addr, r1kh_id, r1kh_key) != 0) { wpa_printf(MSG_DEBUG, "FT: No matching R1KH address found for " "PMK-R1 pull source address " MACSTR, MAC2STR(src_addr)); @@ -1341,7 +1432,7 @@ static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth, plain = ((u8 *) &f) + offsetof(struct ft_r0kh_r1kh_pull_frame, nonce); /* aes_unwrap() does not support inplace decryption, so use a temporary * buffer for the data. */ - if (aes_unwrap(r1kh->key, sizeof(r1kh->key), + if (aes_unwrap(r1kh_key, sizeof(r1kh_key), (FT_R0KH_R1KH_PULL_DATA_LEN + 7) / 8, crypt, plain) < 0) { wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 pull " @@ -1349,6 +1440,16 @@ static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth, return -1; } + /* Ensure that R1KH-ID matches the expected value. */ + if (os_memcmp(r1kh_id, f.r1kh_id, FT_R1KH_ID_LEN) != 0) { + wpa_printf(MSG_ERROR, "FT: PMK-R1 pull included R1KH_ID " + "(" MACSTR ") that did not match the packet " + "senders ID (" MACSTR ", MAC " MACSTR ")", + MAC2STR(f.r1kh_id), MAC2STR(r1kh_id), + MAC2STR(src_addr)); + return -1; + } + wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - nonce", f.nonce, sizeof(f.nonce)); wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - PMKR0Name", @@ -1382,7 +1483,7 @@ static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth, r.pairwise = host_to_le16(pairwise); os_memset(r.pad, 0, sizeof(r.pad)); - if (aes_wrap(r1kh->key, sizeof(r1kh->key), + if (aes_wrap(r1kh_key, sizeof(r1kh_key), (FT_R0KH_R1KH_RESP_DATA_LEN + 7) / 8, r.nonce, resp.nonce) < 0) { os_memset(pmk_r0, 0, PMK_LEN); @@ -1449,7 +1550,7 @@ static int wpa_ft_rrb_rx_resp(struct wpa_authenticator *wpa_auth, struct ft_r0kh_r1kh_resp_frame f; const u8 *crypt; u8 *plain; - struct ft_remote_r0kh *r0kh; + u8 r0kh_key[16]; int pairwise, res; wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 pull response"); @@ -1457,14 +1558,8 @@ static int wpa_ft_rrb_rx_resp(struct wpa_authenticator *wpa_auth, if (data_len < sizeof(f)) return -1; - r0kh = wpa_auth->conf.r0kh_list; - while (r0kh) { - if (os_memcmp(r0kh->addr, src_addr, ETH_ALEN) == 0) - break; - r0kh = r0kh->next; - } - if (r0kh == NULL) { - wpa_printf(MSG_DEBUG, "FT: No matching R0KH address found for " + if (wpa_ft_r0kh_find_by_addr(wpa_auth, src_addr, r0kh_key)) { + wpa_printf(MSG_DEBUG, "FT: No R0KH address found for " "PMK-R0 pull response source address " MACSTR, MAC2STR(src_addr)); return -1; @@ -1475,7 +1570,7 @@ static int wpa_ft_rrb_rx_resp(struct wpa_authenticator *wpa_auth, plain = ((u8 *) &f) + offsetof(struct ft_r0kh_r1kh_resp_frame, nonce); /* aes_unwrap() does not support inplace decryption, so use a temporary * buffer for the data. */ - if (aes_unwrap(r0kh->key, sizeof(r0kh->key), + if (aes_unwrap(r0kh_key, sizeof(r0kh_key), (FT_R0KH_R1KH_RESP_DATA_LEN + 7) / 8, crypt, plain) < 0) { wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 pull " @@ -1518,7 +1613,7 @@ static int wpa_ft_rrb_rx_push(struct wpa_authenticator *wpa_auth, struct ft_r0kh_r1kh_push_frame f; const u8 *crypt; u8 *plain; - struct ft_remote_r0kh *r0kh; + u8 r0kh_key[16]; struct os_time now; os_time_t tsend; int pairwise; @@ -1528,14 +1623,8 @@ static int wpa_ft_rrb_rx_push(struct wpa_authenticator *wpa_auth, if (data_len < sizeof(f)) return -1; - r0kh = wpa_auth->conf.r0kh_list; - while (r0kh) { - if (os_memcmp(r0kh->addr, src_addr, ETH_ALEN) == 0) - break; - r0kh = r0kh->next; - } - if (r0kh == NULL) { - wpa_printf(MSG_DEBUG, "FT: No matching R0KH address found for " + if (wpa_ft_r0kh_find_by_addr(wpa_auth, src_addr, r0kh_key)) { + wpa_printf(MSG_DEBUG, "FT: No R0KH address found for " "PMK-R0 push source address " MACSTR, MAC2STR(src_addr)); return -1; @@ -1547,7 +1636,7 @@ static int wpa_ft_rrb_rx_push(struct wpa_authenticator *wpa_auth, timestamp); /* aes_unwrap() does not support inplace decryption, so use a temporary * buffer for the data. */ - if (aes_unwrap(r0kh->key, sizeof(r0kh->key), + if (aes_unwrap(r0kh_key, sizeof(r0kh_key), (FT_R0KH_R1KH_PUSH_DATA_LEN + 7) / 8, crypt, plain) < 0) { wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 push from " diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c index 2142414..90e39a4 100644 --- a/src/ap/wpa_auth_glue.c +++ b/src/ap/wpa_auth_glue.c @@ -71,6 +71,8 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf, wconf->reassociation_deadline = conf->reassociation_deadline; wconf->r0kh_list = conf->r0kh_list; wconf->r1kh_list = conf->r1kh_list; + wconf->ft_remote_trust_khid = conf->ft_remote_trust_khid; + os_memcpy(wconf->ft_remote_key, conf->ft_remote_key, sizeof(wconf->ft_remote_key)); wconf->pmk_r1_push = conf->pmk_r1_push; wconf->ft_over_ds = conf->ft_over_ds; #endif /* CONFIG_IEEE80211R */ -- 2.9.3 _______________________________________________ Hostap mailing list Hostap@xxxxxxxxxxxxxxxxxxx http://lists.infradead.org/mailman/listinfo/hostap