Enable using FT RRB without configuring each other AP locally. Instead, broadcast messages are exchanged. When an R0KH or R1KH is discovered, it is cached for 1d. When a station uses an invalid or offline r0kh_id, requests are always broadcasted. In order to avoid this, if r0kh does not reply, a temporary blacklist entry is added to r0kh_list. In order to avoid blocking a valid r0kh when a non-existing pmk_r0_name is requested, r0kh is required to always reply using a NAK. Resend requests a few times to ensure blacklisting does not happen due to small packet loss. 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> -- v4: squash wildcard, positive and negative caching into one patch for proper sequence number implementation v3: merge retransmission into this patch and update comment --- hostapd/config_file.c | 8 + hostapd/hostapd.conf | 23 +++ src/ap/ap_config.c | 4 + src/ap/ap_config.h | 4 + src/ap/wpa_auth.c | 3 + src/ap/wpa_auth.h | 12 +- src/ap/wpa_auth_ft.c | 484 +++++++++++++++++++++++++++++++++++++++++++------ src/ap/wpa_auth_glue.c | 8 +- src/ap/wpa_auth_i.h | 1 + 9 files changed, 484 insertions(+), 63 deletions(-) diff --git a/hostapd/config_file.c b/hostapd/config_file.c index 2070a74..8435a36 100644 --- a/hostapd/config_file.c +++ b/hostapd/config_file.c @@ -2636,6 +2636,14 @@ 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, "rkh_neg_timeout") == 0) { + bss->rkh_neg_timeout = atoi(pos); + } else if (os_strcmp(buf, "rkh_pull_timeout") == 0) { + bss->rkh_pull_timeout = atoi(pos); + } else if (os_strcmp(buf, "rkh_pull_retries") == 0) { + bss->rkh_pull_retries = 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 7e61d1c..6f8432a 100644 --- a/hostapd/hostapd.conf +++ b/hostapd/hostapd.conf @@ -1459,6 +1459,10 @@ own_ip_addr=127.0.0.1 #r0kh=02:01:02:03:04:05 r0kh-1.example.com 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f #r0kh=02:01:02:03:04:06 r0kh-2.example.com 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff # And so on.. One line per R0KH. +# Wildcard entry: Upon receiving a response from R0KH, it will be added to this +# list, so subsequent requests won't be broadcasted. +# If R0KH does not reply, it will be blacklisted. +#r0kh=ff:ff:ff:ff:ff:ff * 00112233445566778899aabbccddeeff # List of R1KHs in the same Mobility Domain # format: <MAC address> <R1KH-ID> <256-bit key as hex string> @@ -1468,6 +1472,25 @@ own_ip_addr=127.0.0.1 #r1kh=02:01:02:03:04:05 02:11:22:33:44:55 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f #r1kh=02:01:02:03:04:06 02:11:22:33:44:66 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff # And so on.. One line per R1KH. +# Wildcard entry: Upon receiving a request from an R1KH not yet known, +# it will be added to this list and thus receive push +# notifications. +#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 expire +# Warning: do not cache implies no sequence number validation with wildcards +#rkh_pos_timeout = 86400 (default = 1d) + +# Timeout (milli seconds) for requesting PMK-R1 from R0KH using PULL request +# and number of retries. +#rkh_pull_timeout = 1000 (default = 1s) +#rkh_pull_retries = 4 (default) + +# Timeout (seconds) for non replying R0KH (see wildcard entries above) +# Special values: 0 -> do not cache +# default: 60 seconds +#rkh_neg_timeout = 86400 # Whether PMK-R1 push is enabled at R0KH # 0 = do not push PMK-R1 to all configured R1KHs (default) diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c index 10e1c4b..b889a3c 100644 --- a/src/ap/ap_config.c +++ b/src/ap/ap_config.c @@ -93,6 +93,10 @@ void hostapd_config_defaults_bss(struct hostapd_bss_config *bss) #ifdef CONFIG_IEEE80211R_AP bss->ft_over_ds = 1; + bss->rkh_pos_timeout = 86400; + bss->rkh_neg_timeout = 60; + bss->rkh_pull_timeout = 1000; + bss->rkh_pull_retries = 4; #endif /* CONFIG_IEEE80211R_AP */ bss->radius_das_time_window = 300; diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h index 989b071..0d5274a 100644 --- a/src/ap/ap_config.h +++ b/src/ap/ap_config.h @@ -342,6 +342,10 @@ 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; + int rkh_neg_timeout; + int rkh_pull_timeout; /* ms */ + int rkh_pull_retries; 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 f6e9cde..fe69f11 100644 --- a/src/ap/wpa_auth.c +++ b/src/ap/wpa_auth.c @@ -704,6 +704,9 @@ void wpa_auth_sta_deinit(struct wpa_state_machine *sm) sm->pending_1_of_4_timeout = 0; eloop_cancel_timeout(wpa_sm_call_step, sm, NULL); eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm); +#ifdef CONFIG_IEEE80211R_AP + wpa_ft_sta_deinit(sm); +#endif /* CONFIG_IEEE80211R_AP */ if (sm->in_step_loop) { /* Must not free state machine while wpa_sm_step() is running. * Freeing will be completed in the end of wpa_sm_step(). */ diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h index 49c0480..3e08d81 100644 --- a/src/ap/wpa_auth.h +++ b/src/ap/wpa_auth.h @@ -100,7 +100,8 @@ struct ft_rrb_seq { * auth: * required: SEQ, NONCE, R0KH_ID, R1KH_ID * encrypted: - * required: S1KH_ID, session TLVs + * required: S1KH_ID + * optional: session TLVs * * push frame TLVs: * auth: @@ -182,9 +183,13 @@ 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; + int rkh_neg_timeout; + int rkh_pull_timeout; /* ms */ + int rkh_pull_retries; 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; @@ -367,6 +372,7 @@ void wpa_ft_rrb_oui_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr, 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); +void wpa_ft_sta_deinit(struct wpa_state_machine *sm); #endif /* CONFIG_IEEE80211R_AP */ 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 4a1eeee..4d3a9ef 100644 --- a/src/ap/wpa_auth_ft.c +++ b/src/ap/wpa_auth_ft.c @@ -34,6 +34,8 @@ static int wpa_ft_send_rrb_auth_resp(struct wpa_state_machine *sm, const u8 *current_ap, const u8 *sta_addr, u16 status, const u8 *resp_ies, size_t resp_ies_len); +static void ft_finish_pull(struct wpa_state_machine *sm); +static void wpa_ft_expire_pull(void *eloop_ctx, void *timeout_ctx); static void wpa_ft_rrb_seq_timeout(void *eloop_ctx, void *timeout_ctx); struct tlv_list { @@ -1004,22 +1006,26 @@ static int wpa_ft_rrb_init_r0kh_seq(struct ft_remote_r0kh *r0kh) static void wpa_ft_rrb_lookup_r0kh(struct wpa_authenticator *wpa_auth, const u8 *f_r0kh_id, size_t f_r0kh_id_len, - struct ft_remote_r0kh **r0kh_out) + struct ft_remote_r0kh **r0kh_out, + struct ft_remote_r0kh **r0kh_wildcard) { struct ft_remote_r0kh *r0kh = NULL; + *r0kh_wildcard = NULL; *r0kh_out = NULL; - r0kh = wpa_auth->conf.r0kh_list; + if (wpa_auth->conf.r0kh_list) + r0kh = *wpa_auth->conf.r0kh_list; + for (; r0kh; r0kh = r0kh->next) { + if (r0kh->id_len == 1 && r0kh->id[0] == '*') + *r0kh_wildcard = r0kh; if (f_r0kh_id && r0kh->id_len == f_r0kh_id_len && - os_memcmp_const(f_r0kh_id, r0kh->id, f_r0kh_id_len) == 0) { + os_memcmp_const(f_r0kh_id, r0kh->id, f_r0kh_id_len) == 0) *r0kh_out = r0kh; - break; - } } - if (!*r0kh_out) + if (!*r0kh_out && !*r0kh_wildcard) wpa_printf(MSG_DEBUG, "FT: No matching R0KH found"); if (*r0kh_out && wpa_ft_rrb_init_r0kh_seq(*r0kh_out) < 0) @@ -1047,23 +1053,26 @@ static int wpa_ft_rrb_init_r1kh_seq(struct ft_remote_r1kh *r1kh) static void wpa_ft_rrb_lookup_r1kh(struct wpa_authenticator *wpa_auth, const u8 *f_r1kh_id, - struct ft_remote_r1kh **r1kh_out) - + struct ft_remote_r1kh **r1kh_out, + struct ft_remote_r1kh **r1kh_wildcard) { struct ft_remote_r1kh *r1kh = NULL; + *r1kh_wildcard = NULL; *r1kh_out = NULL; - r1kh = wpa_auth->conf.r1kh_list; + if (wpa_auth->conf.r1kh_list) + r1kh = *wpa_auth->conf.r1kh_list; for (; r1kh; r1kh = r1kh->next) { + if (is_zero_ether_addr(r1kh->addr) && + is_zero_ether_addr(r1kh->id)) + *r1kh_wildcard = r1kh; if (f_r1kh_id && - os_memcmp_const(r1kh->id, f_r1kh_id, FT_R1KH_ID_LEN) == 0) { + os_memcmp_const(r1kh->id, f_r1kh_id, FT_R1KH_ID_LEN) == 0) *r1kh_out = r1kh; - break; - } } - if (!*r1kh_out) + if (!*r1kh_out && !*r1kh_wildcard) wpa_printf(MSG_DEBUG, "FT: No matching R1KH found"); if (*r1kh_out && wpa_ft_rrb_init_r1kh_seq(*r1kh_out) < 0) @@ -1094,6 +1103,158 @@ static int wpa_ft_rrb_check_r1kh(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; + if (r0kh->seq) + wpa_ft_rrb_seq_flush(wpa_auth, r0kh->seq, 0); + os_free(r0kh->seq); + os_free(r0kh); + break; + } +} + + +static void wpa_ft_rrb_r0kh_replenish(struct wpa_authenticator *wpa_auth, + struct ft_remote_r0kh *r0kh, int timeout) +{ + if (timeout > 0) + eloop_replenish_timeout(timeout, 0, wpa_ft_rrb_del_r0kh, + wpa_auth, r0kh); +} + + +static void wpa_ft_rrb_r0kh_timeout(struct wpa_authenticator *wpa_auth, + struct ft_remote_r0kh *r0kh, int timeout) +{ + eloop_cancel_timeout(wpa_ft_rrb_del_r0kh, wpa_auth, r0kh); + + if (timeout > 0) + eloop_register_timeout(timeout, 0, wpa_ft_rrb_del_r0kh, + wpa_auth, r0kh); +} + + +static struct ft_remote_r0kh * +wpa_ft_rrb_add_r0kh(struct wpa_authenticator *wpa_auth, + struct ft_remote_r0kh *r0kh_wildcard, + const u8 *src_addr, const u8 *r0kh_id, size_t id_len, + int timeout) +{ + struct ft_remote_r0kh *r0kh; + + if (!wpa_auth->conf.r0kh_list) + return NULL; + + r0kh = os_zalloc(sizeof(*r0kh)); + if (r0kh == NULL) + return NULL; + + os_memcpy(r0kh->addr, src_addr, sizeof(r0kh->addr)); + + if (id_len > FT_R0KH_ID_MAX_LEN) + id_len = FT_R0KH_ID_MAX_LEN; + os_memcpy(r0kh->id, r0kh_id, id_len); + r0kh->id_len = id_len; + + os_memcpy(r0kh->key, r0kh_wildcard->key, sizeof(r0kh->key)); + + r0kh->next = *wpa_auth->conf.r0kh_list; + *wpa_auth->conf.r0kh_list = r0kh; + + if (timeout > 0) + eloop_register_timeout(timeout, 0, wpa_ft_rrb_del_r0kh, + wpa_auth, r0kh); + + return 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; + if (r1kh->seq) + wpa_ft_rrb_seq_flush(wpa_auth, r1kh->seq, 0); + os_free(r1kh->seq); + os_free(r1kh); + break; + } +} + + +static void wpa_ft_rrb_r1kh_replenish(struct wpa_authenticator *wpa_auth, + struct ft_remote_r1kh *r1kh, int timeout) +{ + if (timeout > 0) + eloop_replenish_timeout(timeout, 0, wpa_ft_rrb_del_r1kh, + wpa_auth, r1kh); +} + + +static struct ft_remote_r1kh * +wpa_ft_rrb_add_r1kh(struct wpa_authenticator *wpa_auth, + struct ft_remote_r1kh *r1kh_wildcard, + const u8 *src_addr, const u8 *r1kh_id, int timeout) +{ + struct ft_remote_r1kh *r1kh; + + if (!wpa_auth->conf.r1kh_list) + return NULL; + + r1kh = os_zalloc(sizeof(*r1kh)); + if (r1kh == NULL) + return NULL; + + 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); + + return r1kh; +} + + +void wpa_ft_sta_deinit(struct wpa_state_machine *sm) +{ + eloop_cancel_timeout(wpa_ft_expire_pull, sm, NULL); +} + + void wpa_ft_deinit_seq_timeout(struct wpa_authenticator *wpa_auth) { struct ft_remote_r0kh *r0kh; @@ -1101,14 +1262,18 @@ void wpa_ft_deinit_seq_timeout(struct wpa_authenticator *wpa_auth) eloop_cancel_timeout(wpa_ft_rrb_seq_timeout, wpa_auth, ELOOP_ALL_CTX); - r0kh = wpa_auth->conf.r0kh_list; + if (!wpa_auth->conf.r0kh_list) + return; + r0kh = *wpa_auth->conf.r0kh_list; while (r0kh) { if (r0kh->seq) wpa_ft_rrb_seq_flush(wpa_auth, r0kh->seq, 0); r0kh = r0kh->next; } - r1kh = wpa_auth->conf.r1kh_list; + if (!wpa_auth->conf.r1kh_list) + return; + r1kh = *wpa_auth->conf.r1kh_list; while (r1kh) { if (r1kh->seq) wpa_ft_rrb_seq_flush(wpa_auth, r1kh->seq, 0); @@ -1120,6 +1285,50 @@ void wpa_ft_deinit_seq_timeout(struct wpa_authenticator *wpa_auth) void wpa_ft_deinit(struct wpa_authenticator *wpa_auth) { wpa_ft_deinit_seq_timeout(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 void wpa_ft_block_r0kh(struct wpa_authenticator *wpa_auth, + const u8 *f_r0kh_id, size_t f_r0kh_id_len) +{ + struct ft_remote_r0kh *r0kh, *r0kh_wildcard; + const u8 *zaddr = (u8 *) "\x00\x00\x00\x00\x00\x00"; + + if (!wpa_auth->conf.rkh_neg_timeout) + return; + + wpa_ft_rrb_lookup_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len, + &r0kh, &r0kh_wildcard); + + if (!r0kh_wildcard) + /* r0kh removed after neg_timeout and might need re-adding */ + return; + + wpa_hexdump(MSG_DEBUG, "FT: Blacklist R0KH-ID", + f_r0kh_id, f_r0kh_id_len); + + if (r0kh) { + wpa_ft_rrb_r0kh_timeout(wpa_auth, r0kh, + wpa_auth->conf.rkh_neg_timeout); + os_memcpy(r0kh->addr, zaddr, ETH_ALEN); + } else + wpa_ft_rrb_add_r0kh(wpa_auth, r0kh_wildcard, zaddr, f_r0kh_id, + f_r0kh_id_len, + wpa_auth->conf.rkh_neg_timeout); +} + +static void wpa_ft_expire_pull(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_state_machine *sm = eloop_ctx; + + if (sm->ft_pending_pull_left_retries <= 0) + wpa_ft_block_r0kh(sm->wpa_auth, sm->r0kh_id, sm->r0kh_id_len); + + /* cancel multiple timeouts */ + eloop_cancel_timeout(wpa_ft_expire_pull, sm, NULL); + ft_finish_pull(sm); } @@ -1127,19 +1336,46 @@ static int wpa_ft_pull_pmk_r1(struct wpa_state_machine *sm, const u8 *ies, size_t ies_len, const u8 *pmk_r0_name) { - struct ft_remote_r0kh *r0kh; + struct ft_remote_r0kh *r0kh, *r0kh_wildcard; u8 *packet = NULL; const u8 *key, *f_r1kh_id = sm->wpa_auth->conf.r1_key_holder; size_t packet_len, key_len; struct ft_rrb_seq f_seq; + int tsecs, tusecs, first; + struct wpabuf *ft_pending_req_ies; + + if (sm->ft_pending_pull_left_retries <= 0) + return -1; + first = (sm->ft_pending_pull_left_retries == + sm->wpa_auth->conf.rkh_pull_retries); + sm->ft_pending_pull_left_retries--; wpa_ft_rrb_lookup_r0kh(sm->wpa_auth, sm->r0kh_id, sm->r0kh_id_len, - &r0kh); + &r0kh, &r0kh_wildcard); + + /* keep r0kh sufficiently long in list for seq num check */ + const int r0kh_timeout = sm->wpa_auth->conf.rkh_pull_timeout / 1000 + + 1 + ftRRBseqTimeout; + if (r0kh) + wpa_ft_rrb_r0kh_replenish(sm->wpa_auth, r0kh, r0kh_timeout); + else if (r0kh_wildcard) { + wpa_printf(MSG_DEBUG, "FT: Using wildcard R0KH-ID"); + /* r0kh->addr: updated by SEQ_RESP and wpa_ft_expire_pull */ + r0kh = wpa_ft_rrb_add_r0kh(sm->wpa_auth, r0kh_wildcard, + r0kh_wildcard->addr, + sm->r0kh_id, sm->r0kh_id_len, + r0kh_timeout); + } if (r0kh == NULL) { wpa_hexdump(MSG_DEBUG, "FT: Did not find R0KH-ID", sm->r0kh_id, sm->r0kh_id_len); return -1; } + if (is_zero_ether_addr(r0kh->addr)) { + wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID is blacklisted", + sm->r0kh_id, sm->r0kh_id_len); + return -1; + } key = r0kh->key; key_len = sizeof(r0kh->key); @@ -1154,7 +1390,8 @@ static int wpa_ft_pull_pmk_r1(struct wpa_state_machine *sm, r0kh->id, r0kh->id_len, f_r1kh_id, key, key_len, NULL, 0, NULL, 0, NULL); - if (random_get_bytes(sm->ft_pending_pull_nonce, FT_RRB_NONCE_LEN) < 0) { + if (first && + random_get_bytes(sm->ft_pending_pull_nonce, FT_RRB_NONCE_LEN) < 0) { wpa_printf(MSG_DEBUG, "FT: Failed to get random data for " "nonce"); return -1; @@ -1190,11 +1427,16 @@ static int wpa_ft_pull_pmk_r1(struct wpa_state_machine *sm, &packet, &packet_len) < 0) return -1; + ft_pending_req_ies = wpabuf_alloc_copy(ies, ies_len); wpabuf_free(sm->ft_pending_req_ies); - sm->ft_pending_req_ies = wpabuf_alloc_copy(ies, ies_len); + sm->ft_pending_req_ies = ft_pending_req_ies; if (sm->ft_pending_req_ies == NULL) return -1; + tsecs = sm->wpa_auth->conf.rkh_pull_timeout / 1000; + tusecs = (sm->wpa_auth->conf.rkh_pull_timeout % 1000) * 1000; + eloop_register_timeout(tsecs, tusecs, wpa_ft_expire_pull, sm, NULL); + wpa_ft_rrb_oui_send(sm->wpa_auth, r0kh->addr, FT_PACKET_R0KH_R1KH_PULL, packet, packet_len); @@ -1809,7 +2051,8 @@ static int wpa_ft_process_auth_req(struct wpa_state_machine *sm, pmk_r1, &pairwise) < 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"); + "PMK-R1 and either unknown or blocked " + "R0KH-ID or NAK from R0KH"); return WLAN_STATUS_INVALID_PMKID; } @@ -1900,6 +2143,7 @@ void wpa_ft_process_auth(struct wpa_state_machine *sm, const u8 *bssid, sm->ft_pending_cb = cb; sm->ft_pending_cb_ctx = ctx; sm->ft_pending_auth_transaction = auth_transaction; + sm->ft_pending_pull_left_retries = sm->wpa_auth->conf.rkh_pull_retries; res = wpa_ft_process_auth_req(sm, ies, ies_len, &resp_ies, &resp_ies_len); if (res < 0) { @@ -2181,6 +2425,7 @@ static int wpa_ft_rrb_rx_request(struct wpa_authenticator *wpa_auth, sm->ft_pending_cb = wpa_ft_rrb_rx_request_cb; sm->ft_pending_cb_ctx = sm; os_memcpy(sm->ft_pending_current_ap, current_ap, ETH_ALEN); + sm->ft_pending_pull_left_retries = sm->wpa_auth->conf.rkh_pull_retries; res = wpa_ft_process_auth_req(sm, body, len, &resp_ies, &resp_ies_len); if (res < 0) { @@ -2259,6 +2504,10 @@ static int wpa_ft_rrb_build_r0(const u8 *key, const size_t key_len, u8 f_pairwise[sizeof(le16)]; int ret; + if (!pmk_r0) + return wpa_ft_rrb_build(key, key_len, tlvs, NULL, tlv_auth, + src_addr, type, packet, packet_len); + if (wpa_derive_pmk_r1(pmk_r0->pmk_r0, pmk_r0->pmk_r0_name, r1kh_id, s1kh_id, pmk_r1, pmk_r1_name) < 0) return -1; @@ -2296,7 +2545,7 @@ static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth, const int type = FT_PACKET_R0KH_R1KH_PULL; u8 *plain = NULL, *packet = NULL; size_t plain_len = 0, packet_len = 0; - struct ft_remote_r1kh *r1kh; + struct ft_remote_r1kh *r1kh, *r1kh_wildcard; const u8 *key = NULL; size_t key_len = 0; int seq_ret; @@ -2321,17 +2570,29 @@ static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth, RRB_GET_AUTH(FT_RRB_R1KH_ID, r1kh_id, msgtype, FT_R1KH_ID_LEN); wpa_printf(MSG_DEBUG, "FT: R1KH-ID=" MACSTR, MAC2STR(f_r1kh_id)); - wpa_ft_rrb_lookup_r1kh(wpa_auth, f_r1kh_id, &r1kh); - if (r1kh == NULL || os_memcmp(r1kh->addr, src_addr, ETH_ALEN) != 0) + wpa_ft_rrb_lookup_r1kh(wpa_auth, f_r1kh_id, &r1kh, &r1kh_wildcard); + if (r1kh) { + key = r1kh->key; + key_len = sizeof(r1kh->key); + } else if (r1kh_wildcard) { + wpa_printf(MSG_DEBUG, "FT: Using wildcard R1KH-ID"); + key = r1kh_wildcard->key; + key_len = sizeof(r1kh_wildcard->key); + } else goto out; - key = r1kh->key; - key_len = sizeof(r1kh->key); RRB_GET_AUTH(FT_RRB_NONCE, nonce, "pull request", FT_RRB_NONCE_LEN); wpa_hexdump(MSG_DEBUG, "FT: nonce", f_nonce, f_nonce_len); - seq_ret = wpa_ft_rrb_seq_chk(r1kh->seq, src_addr, enc, enc_len, auth, - auth_len, msgtype, noDefer); + seq_ret = FT_RRB_SEQ_DROP; + if (r1kh) + seq_ret = wpa_ft_rrb_seq_chk(r1kh->seq, src_addr, enc, enc_len, + auth, auth_len, msgtype, noDefer); + if (!noDefer && r1kh_wildcard && + (!r1kh || os_memcmp(r1kh->addr, src_addr, ETH_ALEN) != 0)) + /* wildcard: r1kh-id unknown or changed addr -> do a seq req */ + seq_ret = FT_RRB_SEQ_DEFER; + if (seq_ret == FT_RRB_SEQ_DROP) goto out; @@ -2339,6 +2600,13 @@ static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth, src_addr, type, &plain, &plain_len) < 0) goto out; + if (!r1kh) + r1kh = wpa_ft_rrb_add_r1kh(wpa_auth, r1kh_wildcard, src_addr, + f_r1kh_id, + wpa_auth->conf.rkh_pos_timeout); + if (!r1kh) + goto out; + if (seq_ret == FT_RRB_SEQ_DEFER) { wpa_ft_rrb_seq_req(wpa_auth, r1kh->seq, src_addr, f_r0kh_id, f_r0kh_id_len, f_r1kh_id, key, key_len, @@ -2349,6 +2617,8 @@ static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth, wpa_ft_rrb_seq_accept(wpa_auth, r1kh->seq, src_addr, auth, auth_len, msgtype); + wpa_ft_rrb_r1kh_replenish(wpa_auth, r1kh, + wpa_auth->conf.rkh_pos_timeout); RRB_GET(FT_RRB_PMK_R0_NAME, pmk_r0_name, msgtype, WPA_PMK_NAME_LEN); wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name", f_pmk_r0_name, @@ -2379,10 +2649,8 @@ static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth, { .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL }, }; - if (wpa_ft_fetch_pmk_r0(wpa_auth, f_s1kh_id, f_pmk_r0_name, &r0) < 0) { + if (wpa_ft_fetch_pmk_r0(wpa_auth, f_s1kh_id, f_pmk_r0_name, &r0) < 0) wpa_printf(MSG_DEBUG, "FT: No matching PMK-R0-Name found"); - goto out; - } ret = wpa_ft_rrb_build_r0(key, key_len, resp, r0, f_r1kh_id, f_s1kh_id, resp_auth, wpa_auth->addr, @@ -2404,6 +2672,7 @@ out: /* @returns 0 on success * -1 on error + * -2 if FR_RRB_PAIRWISE is missing */ static int wpa_ft_rrb_rx_r1(struct wpa_authenticator *wpa_auth, const u8 *src_addr, const u8 type, @@ -2418,7 +2687,7 @@ static int wpa_ft_rrb_rx_r1(struct wpa_authenticator *wpa_auth, { u8 *plain = NULL; size_t plain_len = 0; - struct ft_remote_r0kh *r0kh; + struct ft_remote_r0kh *r0kh, *r0kh_wildcard; const u8 *key; size_t key_len; int seq_ret; @@ -2440,14 +2709,28 @@ static int wpa_ft_rrb_rx_r1(struct wpa_authenticator *wpa_auth, goto out; } - wpa_ft_rrb_lookup_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len, &r0kh); - if (r0kh == NULL || os_memcmp(r0kh->addr, src_addr, ETH_ALEN) != 0) + wpa_ft_rrb_lookup_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len, &r0kh, + &r0kh_wildcard); + if (r0kh) { + key = r0kh->key; + key_len = sizeof(r0kh->key); + } else if (r0kh_wildcard) { + wpa_printf(MSG_DEBUG, "FT: Using wildcard R0KH-ID"); + key = r0kh_wildcard->key; + key_len = sizeof(r0kh_wildcard->key); + } else goto out; - key = r0kh->key; - key_len = sizeof(r0kh->key); - seq_ret = wpa_ft_rrb_seq_chk(r0kh->seq, src_addr, enc, enc_len, auth, - auth_len, msgtype, cb ? 0 : 1); + seq_ret = FT_RRB_SEQ_DROP; + if (r0kh) + seq_ret = wpa_ft_rrb_seq_chk(r0kh->seq, src_addr, enc, enc_len, + auth, auth_len, msgtype, + cb ? 0 : 1); + if (cb && r0kh_wildcard && + (!r0kh || os_memcmp(r0kh->addr, src_addr, ETH_ALEN) != 0)) + /* wildcard: r0kh-id unknown or changed addr -> do a seq req */ + seq_ret = FT_RRB_SEQ_DEFER; + if (seq_ret == FT_RRB_SEQ_DROP) goto out; @@ -2455,6 +2738,13 @@ static int wpa_ft_rrb_rx_r1(struct wpa_authenticator *wpa_auth, src_addr, type, &plain, &plain_len) < 0) goto out; + if (!r0kh) + r0kh = wpa_ft_rrb_add_r0kh(wpa_auth, r0kh_wildcard, src_addr, + f_r0kh_id, f_r0kh_id_len, + wpa_auth->conf.rkh_pos_timeout); + if (!r0kh) + goto out; + if (seq_ret == FT_RRB_SEQ_DEFER) { wpa_ft_rrb_seq_req(wpa_auth, r0kh->seq, src_addr, f_r0kh_id, f_r0kh_id_len, f_r1kh_id, key, key_len, @@ -2464,6 +2754,8 @@ static int wpa_ft_rrb_rx_r1(struct wpa_authenticator *wpa_auth, wpa_ft_rrb_seq_accept(wpa_auth, r0kh->seq, src_addr, auth, auth_len, msgtype); + wpa_ft_rrb_r0kh_replenish(wpa_auth, r0kh, + wpa_auth->conf.rkh_pos_timeout); RRB_GET(FT_RRB_S1KH_ID, s1kh_id, msgtype, ETH_ALEN); wpa_printf(MSG_DEBUG, "FT: S1KH-ID=" MACSTR, MAC2STR(f_s1kh_id)); @@ -2471,9 +2763,11 @@ static int wpa_ft_rrb_rx_r1(struct wpa_authenticator *wpa_auth, if (s1kh_id_out) os_memcpy(s1kh_id_out, f_s1kh_id, ETH_ALEN); + ret = -2; RRB_GET(FT_RRB_PAIRWISE, pairwise, msgtype, sizeof(le16)); wpa_hexdump(MSG_DEBUG, "FT: pairwise", f_pairwise, f_pairwise_len); + ret = -1; RRB_GET(FT_RRB_PMK_R1_NAME, pmk_r1_name, msgtype, WPA_PMK_NAME_LEN); wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", f_pmk_r1_name, WPA_PMK_NAME_LEN); @@ -2506,13 +2800,20 @@ static void ft_finish_pull(struct wpa_state_machine *sm) size_t resp_ies_len; u16 status; + if (sm->ft_pending_cb == NULL || sm->ft_pending_req_ies == NULL) + return; + res = wpa_ft_process_auth_req(sm, wpabuf_head(sm->ft_pending_req_ies), wpabuf_len(sm->ft_pending_req_ies), &resp_ies, &resp_ies_len); + if (res < 0) { + /* this loop is broken by ft_pending_pull_left_retries */ + wpa_printf(MSG_DEBUG, "FT: Callback postponed until response " + "is available"); + return; + } wpabuf_free(sm->ft_pending_req_ies); sm->ft_pending_req_ies = NULL; - if (res < 0) - res = WLAN_STATUS_UNSPECIFIED_FAILURE; status = res; wpa_printf(MSG_DEBUG, "FT: Postponed auth callback result for " MACSTR " - status %u", MAC2STR(sm->addr), status); @@ -2555,7 +2856,7 @@ static int wpa_ft_rrb_rx_resp(struct wpa_authenticator *wpa_auth, { const char *msgtype = "pull response"; const int type = FT_PACKET_R0KH_R1KH_RESP; - int ret = -1; + int nak, ret = -1; struct ft_get_sta_ctx ctx; u8 s1kh_id[ETH_ALEN]; const u8 *f_nonce; @@ -2577,6 +2878,11 @@ static int wpa_ft_rrb_rx_resp(struct wpa_authenticator *wpa_auth, ret = wpa_ft_rrb_rx_r1(wpa_auth, src_addr, type, enc, enc_len, auth, auth_len, msgtype, s1kh_id, noDefer ? NULL : &wpa_ft_rrb_rx_resp); + if (ret == -2) { + ret = 0; + nak = 1; + } else + nak = 0; if (ret < 0) return -1; @@ -2585,6 +2891,9 @@ static int wpa_ft_rrb_rx_resp(struct wpa_authenticator *wpa_auth, wpa_printf(MSG_DEBUG, "FT: Response to a pending pull request " "for " MACSTR " - process from timeout", MAC2STR(ctx.sm->addr)); + eloop_cancel_timeout(wpa_ft_expire_pull, ctx.sm, NULL); + if (nak) + ctx.sm->ft_pending_pull_left_retries = 0; ft_finish_pull(ctx.sm); } @@ -2618,7 +2927,11 @@ static int wpa_ft_rrb_rx_seq(struct wpa_authenticator *wpa_auth, const u8 *enc, size_t enc_len, const u8 *auth, size_t auth_len, struct ft_remote_seq **rkh_seq, - u8 **key, size_t *key_len) + u8 **key, size_t *key_len, + struct ft_remote_r0kh **r0kh_out, + struct ft_remote_r1kh **r1kh_out, + struct ft_remote_r0kh **r0kh_wildcard_out, + struct ft_remote_r1kh **r1kh_wildcard_out) { struct ft_remote_r0kh *r0kh = NULL; struct ft_remote_r1kh *r1kh = NULL; @@ -2627,6 +2940,8 @@ static int wpa_ft_rrb_rx_seq(struct wpa_authenticator *wpa_auth, int to_r0kh, to_r1kh; u8 *plain = NULL; size_t plain_len = 0; + struct ft_remote_r0kh *r0kh_wildcard; + struct ft_remote_r1kh *r1kh_wildcard; RRB_GET_AUTH(FT_RRB_R0KH_ID, r0kh_id, "seq", -1); RRB_GET_AUTH(FT_RRB_R1KH_ID, r1kh_id, "seq", FT_R1KH_ID_LEN); @@ -2646,9 +2961,16 @@ static int wpa_ft_rrb_rx_seq(struct wpa_authenticator *wpa_auth, if (!to_r0kh) { wpa_ft_rrb_lookup_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len, - &r0kh); - if (r0kh == NULL || - os_memcmp(r0kh->addr, src_addr, ETH_ALEN) != 0) { + &r0kh, &r0kh_wildcard); + if (r0kh && !r0kh_wildcard && + os_memcmp(r0kh->addr, src_addr, ETH_ALEN) != 0) + r0kh = NULL; + if (!r0kh && r0kh_wildcard) + r0kh = wpa_ft_rrb_add_r0kh(wpa_auth, r0kh_wildcard, + src_addr, f_r0kh_id, + f_r0kh_id_len, + ftRRBseqTimeout); + if (!r0kh) { wpa_hexdump(MSG_DEBUG, "FT: Did not find R0KH-ID", f_r0kh_id, f_r0kh_id_len); goto out; @@ -2660,9 +2982,16 @@ static int wpa_ft_rrb_rx_seq(struct wpa_authenticator *wpa_auth, } if (!to_r1kh) { - wpa_ft_rrb_lookup_r1kh(wpa_auth, f_r1kh_id, &r1kh); - if (r1kh == NULL || - os_memcmp(r1kh->addr, src_addr, ETH_ALEN) != 0) { + wpa_ft_rrb_lookup_r1kh(wpa_auth, f_r1kh_id, &r1kh, + &r1kh_wildcard); + if (r1kh && !r1kh_wildcard && + os_memcmp(r1kh->addr, src_addr, ETH_ALEN) != 0) + r1kh = NULL; + if (!r1kh && r1kh_wildcard) + r1kh = wpa_ft_rrb_add_r1kh(wpa_auth, r1kh_wildcard, + src_addr, f_r1kh_id, + ftRRBseqTimeout); + if (!r1kh) { wpa_hexdump(MSG_DEBUG, "FT: Did not find R1KH-ID", f_r1kh_id, FT_R1KH_ID_LEN); goto out; @@ -2677,6 +3006,22 @@ static int wpa_ft_rrb_rx_seq(struct wpa_authenticator *wpa_auth, src_addr, type, &plain, &plain_len) < 0) goto out; + if (r0kh) { + wpa_ft_rrb_r0kh_replenish(wpa_auth, r0kh, ftRRBseqTimeout); + if (r0kh_out) + *r0kh_out = r0kh; + if (r0kh_wildcard_out) + *r0kh_wildcard_out = r0kh_wildcard; + } + + if (r1kh) { + wpa_ft_rrb_r1kh_replenish(wpa_auth, r1kh, ftRRBseqTimeout); + if (r1kh_out) + *r1kh_out = r1kh; + if (r1kh_wildcard_out) + *r1kh_wildcard_out = r1kh_wildcard; + } + os_free(plain); plain = NULL; return 0; @@ -2694,7 +3039,7 @@ static int wpa_ft_rrb_rx_seq_req(struct wpa_authenticator *wpa_auth, struct ft_rrb_seq f_seq; const u8 *f_nonce, *f_r0kh_id, *f_r1kh_id; size_t f_nonce_len, f_r0kh_id_len, f_r1kh_id_len; - struct ft_remote_seq *rkh_seq; + struct ft_remote_seq *rkh_seq = NULL; u8 *packet = NULL, *key = NULL; size_t packet_len = 0, key_len = 0; @@ -2702,7 +3047,7 @@ static int wpa_ft_rrb_rx_seq_req(struct wpa_authenticator *wpa_auth, if (wpa_ft_rrb_rx_seq(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_SEQ_REQ, enc, enc_len, auth, auth_len, &rkh_seq, &key, - &key_len) < 0) + &key_len, NULL, NULL, NULL, NULL) < 0) goto out; RRB_GET_AUTH(FT_RRB_NONCE, nonce, "seq request", FT_RRB_NONCE_LEN); @@ -2753,6 +3098,8 @@ static int wpa_ft_rrb_rx_seq_resp(struct wpa_authenticator *wpa_auth, { u8 *key = NULL; size_t key_len = 0; + struct ft_remote_r0kh *r0kh = NULL, *r0kh_wildcard = NULL; + struct ft_remote_r1kh *r1kh = NULL, *r1kh_wildcard = NULL; const u8 *f_nonce, *f_seq; size_t f_nonce_len, f_seq_len; struct ft_remote_seq *rkh_seq = NULL; @@ -2766,14 +3113,15 @@ static int wpa_ft_rrb_rx_seq_resp(struct wpa_authenticator *wpa_auth, if (wpa_ft_rrb_rx_seq(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_SEQ_RESP, enc, enc_len, auth, auth_len, &rkh_seq, &key, - &key_len) < 0) + &key_len, &r0kh, &r1kh, &r0kh_wildcard, + &r1kh_wildcard) < 0) goto out; RRB_GET_AUTH(FT_RRB_NONCE, nonce, "seq response", FT_RRB_NONCE_LEN); wpa_hexdump(MSG_DEBUG, "FT: seq response - nonce", f_nonce, f_nonce_len); - if (!rkh_seq) + if (!rkh_seq) /* e.g. with wildcard */ goto out; found = 0; @@ -2792,6 +3140,20 @@ static int wpa_ft_rrb_rx_seq_resp(struct wpa_authenticator *wpa_auth, goto out; } + if (r0kh) { + wpa_ft_rrb_r0kh_replenish(wpa_auth, r0kh, + wpa_auth->conf.rkh_pos_timeout); + if (r0kh_wildcard) + os_memcpy(r0kh->addr, src_addr, ETH_ALEN); + } + + if (r1kh) { + wpa_ft_rrb_r1kh_replenish(wpa_auth, r1kh, + wpa_auth->conf.rkh_pos_timeout); + if (r1kh_wildcard) + os_memcpy(r1kh->addr, src_addr, ETH_ALEN); + } + seq_ret = wpa_ft_rrb_seq_chk(rkh_seq, src_addr, enc, enc_len, auth, auth_len, "seq response", 1); if (seq_ret == FT_RRB_SEQ_OK) { @@ -2956,6 +3318,7 @@ void wpa_ft_rrb_oui_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr, const u16 *head; const u8 *auth, *enc; size_t alen, elen; + int noDefer = 0; wpa_printf(MSG_DEBUG, "FT: RRB-OUI received frame from remote AP " MACSTR, MAC2STR(src_addr)); @@ -2973,7 +3336,7 @@ void wpa_ft_rrb_oui_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr, "FT: RRB-OUI received frame from remote AP " MACSTR " to multicast address " MACSTR, MAC2STR(src_addr), MAC2STR(dst_addr)); - return; + noDefer = 1; } head = (u16 *) data; @@ -2995,23 +3358,23 @@ void wpa_ft_rrb_oui_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr, switch (oui_suffix) { case FT_PACKET_R0KH_R1KH_PULL: wpa_ft_rrb_rx_pull(wpa_auth, src_addr, enc, elen, auth, alen, - 0); + noDefer); break; case FT_PACKET_R0KH_R1KH_RESP: wpa_ft_rrb_rx_resp(wpa_auth, src_addr, enc, elen, auth, alen, - 0); + noDefer); break; case FT_PACKET_R0KH_R1KH_PUSH: wpa_ft_rrb_rx_push(wpa_auth, src_addr, enc, elen, auth, alen, - 0); + noDefer); break; case FT_PACKET_R0KH_R1KH_SEQ_REQ: wpa_ft_rrb_rx_seq_req(wpa_auth, src_addr, enc, elen, auth, alen, - 0); + noDefer); break; case FT_PACKET_R0KH_R1KH_SEQ_RESP: wpa_ft_rrb_rx_seq_resp(wpa_auth, src_addr, enc, elen, auth, - alen, 0); + alen, noDefer); break; } } @@ -3071,6 +3434,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) { @@ -3086,7 +3451,10 @@ 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; if (wpa_ft_rrb_init_r1kh_seq(r1kh) < 0) continue; wpa_ft_generate_pmk_r1(wpa_auth, r0, r1kh, addr); diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c index d1ff2fb..b8ac616 100644 --- a/src/ap/wpa_auth_glue.c +++ b/src/ap/wpa_auth_glue.c @@ -74,8 +74,12 @@ 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->rkh_neg_timeout = conf->rkh_neg_timeout; + wconf->rkh_pull_timeout = conf->rkh_pull_timeout; + wconf->rkh_pull_retries = conf->rkh_pull_retries; + 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; diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h index 4a9c629..3e6c6a1 100644 --- a/src/ap/wpa_auth_i.h +++ b/src/ap/wpa_auth_i.h @@ -124,6 +124,7 @@ struct wpa_state_machine { u8 ft_pending_pull_nonce[FT_RRB_NONCE_LEN]; u8 ft_pending_auth_transaction; u8 ft_pending_current_ap[ETH_ALEN]; + int ft_pending_pull_left_retries; #endif /* CONFIG_IEEE80211R_AP */ int pending_1_of_4_timeout; -- 2.1.4 _______________________________________________ Hostap mailing list Hostap@xxxxxxxxxxxxxxxxxxx http://lists.infradead.org/mailman/listinfo/hostap