This implements temporary positive caching for R0KH and R1KH seen when using wilcard r0kh/r1kh configuration. Cache entries expire after 1d. In order to free newly created stations later, the r*kh_list start pointer in conf needs to be updateable from wpa_auto_ft.c, where only wconf is accessed. Signed-off-by: Michael Braun <michael-dev@xxxxxxxxxxxxx> --- hostapd/config_file.c | 2 + hostapd/hostapd.conf | 11 +++- src/ap/ap_config.c | 1 + src/ap/ap_config.h | 1 + src/ap/wpa_auth.c | 1 + src/ap/wpa_auth.h | 6 +- src/ap/wpa_auth_ft.c | 155 ++++++++++++++++++++++++++++++++++++++++++++++--- src/ap/wpa_auth_glue.c | 5 +- 8 files changed, 169 insertions(+), 13 deletions(-) diff --git a/hostapd/config_file.c b/hostapd/config_file.c index 2d67282..288cdf1 100644 --- a/hostapd/config_file.c +++ b/hostapd/config_file.c @@ -2543,6 +2543,8 @@ static int hostapd_config_fill(struct hostapd_config *conf, bss->r0_key_lifetime = atoi(pos); } else if (os_strcmp(buf, "reassociation_deadline") == 0) { bss->reassociation_deadline = atoi(pos); + } else if (os_strcmp(buf, "rkh_pos_timeout") == 0) { + bss->rkh_pos_timeout = atoi(pos); } else if (os_strcmp(buf, "r0kh") == 0) { if (add_r0kh(bss, pos) < 0) { wpa_printf(MSG_DEBUG, "Line %d: Invalid r0kh '%s'", diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf index 26546de..c6b8cae 100644 --- a/hostapd/hostapd.conf +++ b/hostapd/hostapd.conf @@ -1306,7 +1306,9 @@ own_ip_addr=127.0.0.1 #r0kh=02:01:02:03:04:05 r0kh-1.example.com 000102030405060708090a0b0c0d0e0f #r0kh=02:01:02:03:04:06 r0kh-2.example.com 00112233445566778899aabbccddeeff # And so on.. One line per R0KH. -# Wildcard entry: +# Wildcard entry: Upon receiving a request from an R1KH not yet known, +# it will be added to this list and thus receive push +# notifications. #r0kh=ff:ff:ff:ff:ff:ff * 00112233445566778899aabbccddeeff # List of R1KHs in the same Mobility Domain @@ -1317,9 +1319,14 @@ own_ip_addr=127.0.0.1 #r1kh=02:01:02:03:04:05 02:11:22:33:44:55 000102030405060708090a0b0c0d0e0f #r1kh=02:01:02:03:04:06 02:11:22:33:44:66 00112233445566778899aabbccddeeff # And so on.. One line per R1KH. -# Wildcard entry: +# Wildcard entry: Upon receiving a response from R0KH, it will be added to this +# list, so subsequent requests won't be broadcasted. #r1kh=00:00:00:00:00:00 00:00:00:00:00:00 00112233445566778899aabbccddeeff +# Timeout (seconds) for newly discovered R0KH/R1KH (see wildcard entries above) +# Special values: 0 -> do not cache; -1 -> do not expire +#rkh_pos_timeout = 86400 (default = 1d) + # Whether PMK-R1 push is enabled at R0KH # 0 = do not push PMK-R1 to all configured R1KHs (default) # 1 = push PMK-R1 to all configured R1KHs whenever a new PMK-R0 is derived diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c index 228de2b..b43890d 100644 --- a/src/ap/ap_config.c +++ b/src/ap/ap_config.c @@ -90,6 +90,7 @@ void hostapd_config_defaults_bss(struct hostapd_bss_config *bss) #ifdef CONFIG_IEEE80211R bss->ft_over_ds = 1; + bss->rkh_pos_timeout = 86400; #endif /* CONFIG_IEEE80211R */ bss->radius_das_time_window = 300; diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h index f9b4306..51f9d5d 100644 --- a/src/ap/ap_config.h +++ b/src/ap/ap_config.h @@ -334,6 +334,7 @@ struct hostapd_bss_config { u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN]; u8 r1_key_holder[FT_R1KH_ID_LEN]; u32 r0_key_lifetime; + int rkh_pos_timeout; u32 reassociation_deadline; struct ft_remote_r0kh *r0kh_list; struct ft_remote_r1kh *r1kh_list; diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c index 3587086..4d88376 100644 --- a/src/ap/wpa_auth.c +++ b/src/ap/wpa_auth.c @@ -516,6 +516,7 @@ void wpa_deinit(struct wpa_authenticator *wpa_auth) #ifdef CONFIG_IEEE80211R wpa_ft_pmk_cache_deinit(wpa_auth->ft_pmk_cache); wpa_auth->ft_pmk_cache = NULL; + wpa_ft_deinit(wpa_auth); #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_P2P diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h index 39a1afd..8abb5e2 100644 --- a/src/ap/wpa_auth.h +++ b/src/ap/wpa_auth.h @@ -168,9 +168,10 @@ struct wpa_auth_config { size_t r0_key_holder_len; u8 r1_key_holder[FT_R1KH_ID_LEN]; u32 r0_key_lifetime; + int rkh_pos_timeout; u32 reassociation_deadline; - struct ft_remote_r0kh *r0kh_list; - struct ft_remote_r1kh *r1kh_list; + struct ft_remote_r0kh **r0kh_list; + struct ft_remote_r1kh **r1kh_list; int pmk_r1_push; int ft_over_ds; int ft_psk_generate_local; @@ -331,6 +332,7 @@ int wpa_ft_action_rx(struct wpa_state_machine *sm, const u8 *data, size_t len); int wpa_ft_rrb_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr, const u8 *data, size_t data_len); void wpa_ft_push_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *addr); +void wpa_ft_deinit(struct wpa_authenticator *wpa_auth); #endif /* CONFIG_IEEE80211R */ void wpa_wnmsleep_rekey_gtk(struct wpa_state_machine *sm); diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c index 04e9bf8..fd727fe 100644 --- a/src/ap/wpa_auth_ft.c +++ b/src/ap/wpa_auth_ft.c @@ -312,6 +312,113 @@ static int wpa_ft_fetch_pmk_r1(struct wpa_authenticator *wpa_auth, } +static void wpa_ft_rrb_del_r0kh(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_authenticator *wpa_auth = eloop_ctx; + struct ft_remote_r0kh *r0kh, *prev = NULL; + + if (!wpa_auth->conf.r0kh_list) + return; + r0kh = *wpa_auth->conf.r0kh_list; + while (r0kh) { + if (r0kh != timeout_ctx) { + r0kh = r0kh->next; + continue; + } + if (prev) + prev->next = r0kh->next; + else + *wpa_auth->conf.r0kh_list = r0kh->next; + os_free(r0kh); + break; + } +} + + +static void wpa_ft_rrb_add_r0kh(struct wpa_authenticator *wpa_auth, + struct ft_remote_r0kh *r0kh_wildcard, + const u8 *src_addr, + u8 *r0kh_id, size_t id_len, int timeout) +{ + struct ft_remote_r0kh *r0kh; + + if (!wpa_auth->conf.r0kh_list) + return; + + r0kh = os_zalloc(sizeof(*r0kh)); + if (r0kh == NULL) + return; + + os_memcpy(r0kh->addr, src_addr, sizeof(r0kh->addr)); + os_memcpy(r0kh->id, r0kh_id, sizeof(r0kh->id)); + r0kh->id_len = id_len; + if (r0kh_wildcard) + os_memcpy(r0kh->key, r0kh_wildcard->key, sizeof(r0kh->key)); + r0kh->next = *wpa_auth->conf.r0kh_list; + *wpa_auth->conf.r0kh_list = r0kh; + timeout = wpa_auth->conf.rkh_pos_timeout; + + if (timeout > 0) + eloop_register_timeout(timeout, 0, wpa_ft_rrb_del_r0kh, + wpa_auth, r0kh); +} + + +static void wpa_ft_rrb_del_r1kh(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_authenticator *wpa_auth = eloop_ctx; + struct ft_remote_r1kh *r1kh, *prev = NULL; + + if (!wpa_auth->conf.r1kh_list) + return; + r1kh = *wpa_auth->conf.r1kh_list; + while (r1kh) { + if (r1kh != timeout_ctx) { + r1kh = r1kh->next; + continue; + } + if (prev) + prev->next = r1kh->next; + else + *wpa_auth->conf.r1kh_list = r1kh->next; + os_free(r1kh); + break; + } +} + + +static void wpa_ft_rrb_add_r1kh(struct wpa_authenticator *wpa_auth, + struct ft_remote_r1kh *r1kh_wildcard, + const u8 *src_addr, u8 *r1kh_id, int timeout) +{ + struct ft_remote_r1kh *r1kh; + + if (!wpa_auth->conf.r1kh_list) + return; + + r1kh = os_zalloc(sizeof(*r1kh)); + if (r1kh == NULL) + return; + + os_memcpy(r1kh->addr, src_addr, sizeof(r1kh->addr)); + os_memcpy(r1kh->id, r1kh_id, sizeof(r1kh->id)); + os_memcpy(r1kh->key, r1kh_wildcard->key, sizeof(r1kh->key)); + r1kh->next = *wpa_auth->conf.r1kh_list; + *wpa_auth->conf.r1kh_list = r1kh; + + if (timeout > 0) + eloop_register_timeout(timeout, 0, wpa_ft_rrb_del_r1kh, + wpa_auth, r1kh); +} + + +void wpa_ft_deinit(struct wpa_authenticator *wpa_auth) +{ + eloop_cancel_timeout(wpa_ft_rrb_del_r1kh, wpa_auth, ELOOP_ALL_CTX); + eloop_cancel_timeout(wpa_ft_rrb_del_r0kh, wpa_auth, ELOOP_ALL_CTX); +} + + static int wpa_ft_pull_pmk_r1(struct wpa_state_machine *sm, const u8 *ies, size_t ies_len, const u8 *pmk_r0_name) @@ -319,7 +426,9 @@ static int wpa_ft_pull_pmk_r1(struct wpa_state_machine *sm, struct ft_remote_r0kh *r0kh, *r0kh_wildcard = NULL; struct ft_r0kh_r1kh_pull_frame frame, f; - r0kh = sm->wpa_auth->conf.r0kh_list; + if (!sm->wpa_auth->conf.r0kh_list) + return -1; + r0kh = *sm->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) == @@ -1441,7 +1550,9 @@ 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; + if (!wpa_auth->conf.r1kh_list) + return -1; + r1kh = *wpa_auth->conf.r1kh_list; while (r1kh) { if (os_memcmp(r1kh->addr, src_addr, ETH_ALEN) == 0) break; @@ -1486,6 +1597,10 @@ static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth, wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull - R1KH-ID=" MACSTR " S1KH-ID=" MACSTR, MAC2STR(f.r1kh_id), MAC2STR(f.s1kh_id)); + if (r1kh == r1kh_wildcard && wpa_auth->conf.rkh_pos_timeout) + wpa_ft_rrb_add_r1kh(wpa_auth, r1kh_wildcard, src_addr, + f.r1kh_id, wpa_auth->conf.rkh_pos_timeout); + os_memset(&resp, 0, sizeof(resp)); resp.frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB; resp.packet_type = FT_PACKET_R0KH_R1KH_RESP; @@ -1553,9 +1668,16 @@ static void ft_pull_resp_cb_finish(void *eloop_ctx, void *timeout_ctx) } +struct ft_pull_resp_cb_ctx { + struct ft_r0kh_r1kh_resp_frame *frame; + struct ft_remote_r0kh *r0kh_wildcard; + u8 src_addr[ETH_ALEN]; +}; + static int ft_pull_resp_cb(struct wpa_state_machine *sm, void *ctx) { - struct ft_r0kh_r1kh_resp_frame *frame = ctx; + struct ft_pull_resp_cb_ctx *info = ctx; + struct ft_r0kh_r1kh_resp_frame *frame = info->frame; if (os_memcmp(frame->s1kh_id, sm->addr, ETH_ALEN) != 0) return 0; @@ -1568,6 +1690,13 @@ static int ft_pull_resp_cb(struct wpa_state_machine *sm, void *ctx) wpa_printf(MSG_DEBUG, "FT: Response to a pending pull request for " MACSTR " - process from timeout", MAC2STR(sm->addr)); eloop_register_timeout(0, 0, ft_pull_resp_cb_finish, sm, NULL); + + if (info->r0kh_wildcard && sm->wpa_auth->conf.rkh_pos_timeout) + wpa_ft_rrb_add_r0kh(sm->wpa_auth, info->r0kh_wildcard, + info->src_addr, sm->r0kh_id, + sm->r0kh_id_len, + sm->wpa_auth->conf.rkh_pos_timeout); + return 1; } @@ -1581,13 +1710,16 @@ static int wpa_ft_rrb_rx_resp(struct wpa_authenticator *wpa_auth, u8 *plain; struct ft_remote_r0kh *r0kh, *r0kh_wildcard = NULL; int pairwise, res; + struct ft_pull_resp_cb_ctx ctx; wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 pull response"); if (data_len < sizeof(f)) return -1; - r0kh = wpa_auth->conf.r0kh_list; + if (!wpa_auth->conf.r0kh_list) + return -1; + r0kh = *wpa_auth->conf.r0kh_list; while (r0kh) { if (os_memcmp(r0kh->addr, src_addr, ETH_ALEN) == 0) break; @@ -1640,7 +1772,12 @@ static int wpa_ft_rrb_rx_resp(struct wpa_authenticator *wpa_auth, res = wpa_ft_store_pmk_r1(wpa_auth, f.s1kh_id, f.pmk_r1, f.pmk_r1_name, pairwise); wpa_printf(MSG_DEBUG, "FT: Look for pending pull request"); - wpa_auth_for_each_sta(wpa_auth, ft_pull_resp_cb, &f); + + ctx.frame = &f; + os_memcpy(ctx.src_addr, src_addr, ETH_ALEN); + ctx.r0kh_wildcard = (r0kh == r0kh_wildcard) ? r0kh_wildcard : NULL; + + wpa_auth_for_each_sta(wpa_auth, ft_pull_resp_cb, &ctx); os_memset(f.pmk_r1, 0, PMK_LEN); return res ? 0 : -1; @@ -1664,7 +1801,9 @@ 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; + if (!wpa_auth->conf.r0kh_list) + return -1; + r0kh = *wpa_auth->conf.r0kh_list; while (r0kh) { if (os_memcmp(r0kh->addr, src_addr, ETH_ALEN) == 0) break; @@ -1909,6 +2048,8 @@ void wpa_ft_push_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *addr) if (!wpa_auth->conf.pmk_r1_push) return; + if (!wpa_auth->conf.r1kh_list) + return; r0 = wpa_auth->ft_pmk_cache->pmk_r0; while (r0) { @@ -1924,7 +2065,7 @@ void wpa_ft_push_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *addr) wpa_printf(MSG_DEBUG, "FT: Deriving and pushing PMK-R1 keys to R1KHs " "for STA " MACSTR, MAC2STR(addr)); - for (r1kh = wpa_auth->conf.r1kh_list; r1kh; r1kh = r1kh->next) { + for (r1kh = *wpa_auth->conf.r1kh_list; r1kh; r1kh = r1kh->next) { if (is_zero_ether_addr(r1kh->addr) || is_zero_ether_addr(r1kh->id)) continue; diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c index 12b9ccc..c99aefc 100644 --- a/src/ap/wpa_auth_glue.c +++ b/src/ap/wpa_auth_glue.c @@ -69,8 +69,9 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf, os_memcpy(wconf->r1_key_holder, conf->r1_key_holder, FT_R1KH_ID_LEN); wconf->r0_key_lifetime = conf->r0_key_lifetime; wconf->reassociation_deadline = conf->reassociation_deadline; - wconf->r0kh_list = conf->r0kh_list; - wconf->r1kh_list = conf->r1kh_list; + wconf->rkh_pos_timeout = conf->rkh_pos_timeout; + wconf->r0kh_list = &conf->r0kh_list; + wconf->r1kh_list = &conf->r1kh_list; wconf->pmk_r1_push = conf->pmk_r1_push; wconf->ft_over_ds = conf->ft_over_ds; wconf->ft_psk_generate_local = conf->ft_psk_generate_local; -- 2.1.4 _______________________________________________ Hostap mailing list Hostap@xxxxxxxxxxxxxxxxxxx http://lists.infradead.org/mailman/listinfo/hostap