FT: Add support for wildcard R0KH/R1KH

Enable use of FT RRB without configuring each other AP locally. Instead,
broadcast messages are exchanged to discover APs within the local
network.

When an R0KH or R1KH is discovered, it is cached for one day.

When a station uses an invalid or offline r0kh_id, requests are always
broadcast. In order to avoid this, if r0kh does not reply, a temporary
blacklist entry is added to r0kh_list.

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.

To free newly created stations later, the r*kh_list start pointer in
conf needs to be updateable from wpa_auth_ft.c, where only wconf is
accessed.

Signed-off-by: Michael Braun <michael-dev@fami-braun.de>
This commit is contained in:
Michael Braun 2017-04-02 14:52:52 +02:00 committed by Jouni Malinen
parent ba88dd65e7
commit 3a46cf93d0
9 changed files with 570 additions and 68 deletions

View file

@ -2641,6 +2641,14 @@ static int hostapd_config_fill(struct hostapd_config *conf,
bss->r0_key_lifetime = atoi(pos); bss->r0_key_lifetime = atoi(pos);
} else if (os_strcmp(buf, "reassociation_deadline") == 0) { } else if (os_strcmp(buf, "reassociation_deadline") == 0) {
bss->reassociation_deadline = atoi(pos); 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) { } else if (os_strcmp(buf, "r0kh") == 0) {
if (add_r0kh(bss, pos) < 0) { if (add_r0kh(bss, pos) < 0) {
wpa_printf(MSG_DEBUG, "Line %d: Invalid r0kh '%s'", wpa_printf(MSG_DEBUG, "Line %d: Invalid r0kh '%s'",

View file

@ -1459,6 +1459,11 @@ 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:05 r0kh-1.example.com 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f
#r0kh=02:01:02:03:04:06 r0kh-2.example.com 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff #r0kh=02:01:02:03:04:06 r0kh-2.example.com 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff
# And so on.. One line per R0KH. # 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 broadcast. 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 # List of R1KHs in the same Mobility Domain
# format: <MAC address> <R1KH-ID> <256-bit key as hex string> # format: <MAC address> <R1KH-ID> <256-bit key as hex string>
@ -1468,6 +1473,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:05 02:11:22:33:44:55 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f
#r1kh=02:01:02:03:04:06 02:11:22:33:44:66 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff #r1kh=02:01:02:03:04:06 02:11:22:33:44:66 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff
# And so on.. One line per R1KH. # 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 will 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 = 1 day)
# Timeout (milliseconds) for requesting PMK-R1 from R0KH using PULL request
# and number of retries.
#rkh_pull_timeout=1000 (default = 1 second)
#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=60
# Note: The R0KH/R1KH keys used to be 128-bit in length before the message # Note: The R0KH/R1KH keys used to be 128-bit in length before the message
# format was changed. That shorter key length is still supported for backwards # format was changed. That shorter key length is still supported for backwards

View file

@ -93,6 +93,10 @@ void hostapd_config_defaults_bss(struct hostapd_bss_config *bss)
#ifdef CONFIG_IEEE80211R_AP #ifdef CONFIG_IEEE80211R_AP
bss->ft_over_ds = 1; 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 */ #endif /* CONFIG_IEEE80211R_AP */
bss->radius_das_time_window = 300; bss->radius_das_time_window = 300;

View file

@ -342,6 +342,10 @@ struct hostapd_bss_config {
u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN]; u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN];
u8 r1_key_holder[FT_R1KH_ID_LEN]; u8 r1_key_holder[FT_R1KH_ID_LEN];
u32 r0_key_lifetime; u32 r0_key_lifetime;
int rkh_pos_timeout;
int rkh_neg_timeout;
int rkh_pull_timeout; /* ms */
int rkh_pull_retries;
u32 reassociation_deadline; u32 reassociation_deadline;
struct ft_remote_r0kh *r0kh_list; struct ft_remote_r0kh *r0kh_list;
struct ft_remote_r1kh *r1kh_list; struct ft_remote_r1kh *r1kh_list;

View file

@ -706,6 +706,9 @@ void wpa_auth_sta_deinit(struct wpa_state_machine *sm)
sm->pending_1_of_4_timeout = 0; sm->pending_1_of_4_timeout = 0;
eloop_cancel_timeout(wpa_sm_call_step, sm, NULL); eloop_cancel_timeout(wpa_sm_call_step, sm, NULL);
eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm); 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) { if (sm->in_step_loop) {
/* Must not free state machine while wpa_sm_step() is running. /* Must not free state machine while wpa_sm_step() is running.
* Freeing will be completed in the end of wpa_sm_step(). */ * Freeing will be completed in the end of wpa_sm_step(). */

View file

@ -103,7 +103,8 @@ struct ft_rrb_seq {
* auth: * auth:
* required: SEQ, NONCE, R0KH_ID, R1KH_ID * required: SEQ, NONCE, R0KH_ID, R1KH_ID
* encrypted: * encrypted:
* required: S1KH_ID, session TLVs * required: S1KH_ID
* optional: session TLVs
* *
* push frame TLVs: * push frame TLVs:
* auth: * auth:
@ -185,9 +186,13 @@ struct wpa_auth_config {
size_t r0_key_holder_len; size_t r0_key_holder_len;
u8 r1_key_holder[FT_R1KH_ID_LEN]; u8 r1_key_holder[FT_R1KH_ID_LEN];
u32 r0_key_lifetime; u32 r0_key_lifetime;
int rkh_pos_timeout;
int rkh_neg_timeout;
int rkh_pull_timeout; /* ms */
int rkh_pull_retries;
u32 reassociation_deadline; u32 reassociation_deadline;
struct ft_remote_r0kh *r0kh_list; struct ft_remote_r0kh **r0kh_list;
struct ft_remote_r1kh *r1kh_list; struct ft_remote_r1kh **r1kh_list;
int pmk_r1_push; int pmk_r1_push;
int ft_over_ds; int ft_over_ds;
int ft_psk_generate_local; int ft_psk_generate_local;
@ -370,6 +375,7 @@ void wpa_ft_rrb_oui_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr,
size_t data_len); size_t data_len);
void wpa_ft_push_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *addr); 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_deinit(struct wpa_authenticator *wpa_auth);
void wpa_ft_sta_deinit(struct wpa_state_machine *sm);
#endif /* CONFIG_IEEE80211R_AP */ #endif /* CONFIG_IEEE80211R_AP */
void wpa_wnmsleep_rekey_gtk(struct wpa_state_machine *sm); void wpa_wnmsleep_rekey_gtk(struct wpa_state_machine *sm);

View file

@ -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, const u8 *current_ap, const u8 *sta_addr,
u16 status, const u8 *resp_ies, u16 status, const u8 *resp_ies,
size_t resp_ies_len); 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); static void wpa_ft_rrb_seq_timeout(void *eloop_ctx, void *timeout_ctx);
struct tlv_list { struct tlv_list {
@ -1008,21 +1010,27 @@ 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, static void wpa_ft_rrb_lookup_r0kh(struct wpa_authenticator *wpa_auth,
const u8 *f_r0kh_id, size_t f_r0kh_id_len, 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; struct ft_remote_r0kh *r0kh;
*r0kh_wildcard = NULL;
*r0kh_out = NULL; *r0kh_out = NULL;
for (r0kh = wpa_auth->conf.r0kh_list; r0kh; r0kh = r0kh->next) { if (wpa_auth->conf.r0kh_list)
r0kh = *wpa_auth->conf.r0kh_list;
else
r0kh = NULL;
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 && 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; *r0kh_out = r0kh;
break;
}
} }
if (!*r0kh_out) if (!*r0kh_out && !*r0kh_wildcard)
wpa_printf(MSG_DEBUG, "FT: No matching R0KH found"); wpa_printf(MSG_DEBUG, "FT: No matching R0KH found");
if (*r0kh_out && wpa_ft_rrb_init_r0kh_seq(*r0kh_out) < 0) if (*r0kh_out && wpa_ft_rrb_init_r0kh_seq(*r0kh_out) < 0)
@ -1049,21 +1057,28 @@ 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, static void wpa_ft_rrb_lookup_r1kh(struct wpa_authenticator *wpa_auth,
const u8 *f_r1kh_id, 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; struct ft_remote_r1kh *r1kh;
*r1kh_wildcard = NULL;
*r1kh_out = NULL; *r1kh_out = NULL;
for (r1kh = wpa_auth->conf.r1kh_list; r1kh; r1kh = r1kh->next) { if (wpa_auth->conf.r1kh_list)
r1kh = *wpa_auth->conf.r1kh_list;
else
r1kh = NULL;
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 && 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; *r1kh_out = r1kh;
break;
}
} }
if (!*r1kh_out) if (!*r1kh_out && !*r1kh_wildcard)
wpa_printf(MSG_DEBUG, "FT: No matching R1KH found"); wpa_printf(MSG_DEBUG, "FT: No matching R1KH found");
if (*r1kh_out && wpa_ft_rrb_init_r1kh_seq(*r1kh_out) < 0) if (*r1kh_out && wpa_ft_rrb_init_r1kh_seq(*r1kh_out) < 0)
@ -1094,6 +1109,163 @@ 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;
for (r0kh = *wpa_auth->conf.r0kh_list; r0kh; r0kh = r0kh->next) {
if (r0kh == timeout_ctx)
break;
prev = r0kh;
}
if (!r0kh)
return;
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);
}
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)
return NULL;
if (src_addr)
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);
if (wpa_ft_rrb_init_r0kh_seq(r0kh) < 0)
return NULL;
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;
for (r1kh = *wpa_auth->conf.r1kh_list; r1kh; r1kh = r1kh->next) {
if (r1kh == timeout_ctx)
break;
prev = r1kh;
}
if (!r1kh)
return;
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);
}
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)
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);
if (wpa_ft_rrb_init_r1kh_seq(r1kh) < 0)
return NULL;
return r1kh;
}
void wpa_ft_sta_deinit(struct wpa_state_machine *sm)
{
eloop_cancel_timeout(wpa_ft_expire_pull, sm, NULL);
}
static void wpa_ft_deinit_seq(struct wpa_authenticator *wpa_auth) static void wpa_ft_deinit_seq(struct wpa_authenticator *wpa_auth)
{ {
struct ft_remote_r0kh *r0kh; struct ft_remote_r0kh *r0kh;
@ -1101,7 +1273,11 @@ static void wpa_ft_deinit_seq(struct wpa_authenticator *wpa_auth)
eloop_cancel_timeout(wpa_ft_rrb_seq_timeout, wpa_auth, ELOOP_ALL_CTX); eloop_cancel_timeout(wpa_ft_rrb_seq_timeout, wpa_auth, ELOOP_ALL_CTX);
for (r0kh = wpa_auth->conf.r0kh_list; r0kh; r0kh = r0kh->next) { if (wpa_auth->conf.r0kh_list)
r0kh = *wpa_auth->conf.r0kh_list;
else
r0kh = NULL;
for (; r0kh; r0kh = r0kh->next) {
if (!r0kh->seq) if (!r0kh->seq)
continue; continue;
wpa_ft_rrb_seq_flush(wpa_auth, r0kh->seq, 0); wpa_ft_rrb_seq_flush(wpa_auth, r0kh->seq, 0);
@ -1109,7 +1285,11 @@ static void wpa_ft_deinit_seq(struct wpa_authenticator *wpa_auth)
r0kh->seq = NULL; r0kh->seq = NULL;
} }
for (r1kh = wpa_auth->conf.r1kh_list; r1kh; r1kh = r1kh->next) { if (wpa_auth->conf.r1kh_list)
r1kh = *wpa_auth->conf.r1kh_list;
else
r1kh = NULL;
for (; r1kh; r1kh = r1kh->next) {
if (!r1kh->seq) if (!r1kh->seq)
continue; continue;
wpa_ft_rrb_seq_flush(wpa_auth, r1kh->seq, 0); wpa_ft_rrb_seq_flush(wpa_auth, r1kh->seq, 0);
@ -1119,9 +1299,100 @@ static void wpa_ft_deinit_seq(struct wpa_authenticator *wpa_auth)
} }
static void wpa_ft_deinit_rkh_tmp(struct wpa_authenticator *wpa_auth)
{
struct ft_remote_r0kh *r0kh, *r0kh_next, *r0kh_prev = NULL;
struct ft_remote_r1kh *r1kh, *r1kh_next, *r1kh_prev = NULL;
if (wpa_auth->conf.r0kh_list)
r0kh = *wpa_auth->conf.r0kh_list;
else
r0kh = NULL;
while (r0kh) {
r0kh_next = r0kh->next;
if (eloop_cancel_timeout(wpa_ft_rrb_del_r0kh, wpa_auth,
r0kh) > 0) {
if (r0kh_prev)
r0kh_prev->next = r0kh_next;
else
*wpa_auth->conf.r0kh_list = r0kh_next;
os_free(r0kh);
} else {
r0kh_prev = r0kh;
}
r0kh = r0kh_next;
}
if (wpa_auth->conf.r1kh_list)
r1kh = *wpa_auth->conf.r1kh_list;
else
r1kh = NULL;
while (r1kh) {
r1kh_next = r1kh->next;
if (eloop_cancel_timeout(wpa_ft_rrb_del_r1kh, wpa_auth,
r1kh) > 0) {
if (r1kh_prev)
r1kh_prev->next = r1kh_next;
else
*wpa_auth->conf.r1kh_list = r1kh_next;
os_free(r1kh);
} else {
r1kh_prev = r1kh;
}
r1kh = r1kh_next;
}
}
void wpa_ft_deinit(struct wpa_authenticator *wpa_auth) void wpa_ft_deinit(struct wpa_authenticator *wpa_auth)
{ {
wpa_ft_deinit_seq(wpa_auth); wpa_ft_deinit_seq(wpa_auth);
wpa_ft_deinit_rkh_tmp(wpa_auth);
}
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;
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_memset(r0kh->addr, 0, ETH_ALEN);
} else
wpa_ft_rrb_add_r0kh(wpa_auth, r0kh_wildcard, NULL, 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;
wpa_printf(MSG_DEBUG, "FT: Timeout pending pull request for " MACSTR,
MAC2STR(sm->addr));
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);
} }
@ -1129,11 +1400,14 @@ static int wpa_ft_pull_pmk_r1(struct wpa_state_machine *sm,
const u8 *ies, size_t ies_len, const u8 *ies, size_t ies_len,
const u8 *pmk_r0_name) const u8 *pmk_r0_name)
{ {
struct ft_remote_r0kh *r0kh; struct ft_remote_r0kh *r0kh, *r0kh_wildcard;
u8 *packet = NULL; u8 *packet = NULL;
const u8 *key, *f_r1kh_id = sm->wpa_auth->conf.r1_key_holder; const u8 *key, *f_r1kh_id = sm->wpa_auth->conf.r1_key_holder;
size_t packet_len, key_len; size_t packet_len, key_len;
struct ft_rrb_seq f_seq; struct ft_rrb_seq f_seq;
int tsecs, tusecs, first;
struct wpabuf *ft_pending_req_ies;
int r0kh_timeout;
struct tlv_list req_enc[] = { struct tlv_list req_enc[] = {
{ .type = FT_RRB_PMK_R0_NAME, .len = WPA_PMK_NAME_LEN, { .type = FT_RRB_PMK_R0_NAME, .len = WPA_PMK_NAME_LEN,
.data = pmk_r0_name }, .data = pmk_r0_name },
@ -1153,13 +1427,38 @@ static int wpa_ft_pull_pmk_r1(struct wpa_state_machine *sm,
{ .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL }, { .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL },
}; };
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, wpa_ft_rrb_lookup_r0kh(sm->wpa_auth, sm->r0kh_id, sm->r0kh_id_len,
&r0kh); &r0kh, &r0kh_wildcard);
/* Keep r0kh sufficiently long in the list for seq num check */
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) { if (r0kh == NULL) {
wpa_hexdump(MSG_DEBUG, "FT: Did not find R0KH-ID", wpa_hexdump(MSG_DEBUG, "FT: Did not find R0KH-ID",
sm->r0kh_id, sm->r0kh_id_len); sm->r0kh_id, sm->r0kh_id_len);
return -1; 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 = r0kh->key;
key_len = sizeof(r0kh->key); key_len = sizeof(r0kh->key);
@ -1175,7 +1474,8 @@ static int wpa_ft_pull_pmk_r1(struct wpa_state_machine *sm,
key_len, NULL, 0, NULL, 0, NULL); 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 " wpa_printf(MSG_DEBUG, "FT: Failed to get random data for "
"nonce"); "nonce");
return -1; return -1;
@ -1191,13 +1491,18 @@ static int wpa_ft_pull_pmk_r1(struct wpa_state_machine *sm,
&packet, &packet_len) < 0) &packet, &packet_len) < 0)
return -1; return -1;
ft_pending_req_ies = wpabuf_alloc_copy(ies, ies_len);
wpabuf_free(sm->ft_pending_req_ies); 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) { if (!sm->ft_pending_req_ies) {
os_free(packet); os_free(packet);
return -1; 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, wpa_ft_rrb_oui_send(sm->wpa_auth, r0kh->addr, FT_PACKET_R0KH_R1KH_PULL,
packet, packet_len); packet, packet_len);
@ -1816,8 +2121,8 @@ static int wpa_ft_process_auth_req(struct wpa_state_machine *sm,
} else if (wpa_ft_fetch_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1_name, } else if (wpa_ft_fetch_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1_name,
pmk_r1, &pairwise) < 0) { pmk_r1, &pairwise) < 0) {
if (wpa_ft_pull_pmk_r1(sm, ies, ies_len, parse.rsn_pmkid) < 0) { if (wpa_ft_pull_pmk_r1(sm, ies, ies_len, parse.rsn_pmkid) < 0) {
wpa_printf(MSG_DEBUG, "FT: Did not have matching " wpa_printf(MSG_DEBUG,
"PMK-R1 and unknown R0KH-ID"); "FT: Did not have matching PMK-R1 and either unknown or blocked R0KH-ID or NAK from R0KH");
return WLAN_STATUS_INVALID_PMKID; return WLAN_STATUS_INVALID_PMKID;
} }
@ -1908,6 +2213,7 @@ void wpa_ft_process_auth(struct wpa_state_machine *sm, const u8 *bssid,
sm->ft_pending_cb = cb; sm->ft_pending_cb = cb;
sm->ft_pending_cb_ctx = ctx; sm->ft_pending_cb_ctx = ctx;
sm->ft_pending_auth_transaction = auth_transaction; 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, res = wpa_ft_process_auth_req(sm, ies, ies_len, &resp_ies,
&resp_ies_len); &resp_ies_len);
if (res < 0) { if (res < 0) {
@ -2189,6 +2495,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 = wpa_ft_rrb_rx_request_cb;
sm->ft_pending_cb_ctx = sm; sm->ft_pending_cb_ctx = sm;
os_memcpy(sm->ft_pending_current_ap, current_ap, ETH_ALEN); 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, res = wpa_ft_process_auth_req(sm, body, len, &resp_ies,
&resp_ies_len); &resp_ies_len);
if (res < 0) { if (res < 0) {
@ -2276,6 +2583,10 @@ static int wpa_ft_rrb_build_r0(const u8 *key, const size_t key_len,
{ .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL }, { .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL },
}; };
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, if (wpa_derive_pmk_r1(pmk_r0->pmk_r0, pmk_r0->pmk_r0_name, r1kh_id,
s1kh_id, pmk_r1, pmk_r1_name) < 0) s1kh_id, pmk_r1, pmk_r1_name) < 0)
return -1; return -1;
@ -2301,7 +2612,7 @@ static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth,
const char *msgtype = "pull request"; const char *msgtype = "pull request";
u8 *plain = NULL, *packet = NULL; u8 *plain = NULL, *packet = NULL;
size_t plain_len = 0, packet_len = 0; size_t plain_len = 0, packet_len = 0;
struct ft_remote_r1kh *r1kh; struct ft_remote_r1kh *r1kh, *r1kh_wildcard;
const u8 *key; const u8 *key;
size_t key_len; size_t key_len;
int seq_ret; int seq_ret;
@ -2327,17 +2638,31 @@ 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); 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_printf(MSG_DEBUG, "FT: R1KH-ID=" MACSTR, MAC2STR(f_r1kh_id));
wpa_ft_rrb_lookup_r1kh(wpa_auth, f_r1kh_id, &r1kh); wpa_ft_rrb_lookup_r1kh(wpa_auth, f_r1kh_id, &r1kh, &r1kh_wildcard);
if (!r1kh || os_memcmp(r1kh->addr, src_addr, ETH_ALEN) != 0) if (r1kh) {
goto out;
key = r1kh->key; key = r1kh->key;
key_len = sizeof(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;
}
RRB_GET_AUTH(FT_RRB_NONCE, nonce, "pull request", FT_RRB_NONCE_LEN); RRB_GET_AUTH(FT_RRB_NONCE, nonce, "pull request", FT_RRB_NONCE_LEN);
wpa_hexdump(MSG_DEBUG, "FT: nonce", f_nonce, f_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, seq_ret = FT_RRB_SEQ_DROP;
auth_len, msgtype, no_defer); if (r1kh)
seq_ret = wpa_ft_rrb_seq_chk(r1kh->seq, src_addr, enc, enc_len,
auth, auth_len, msgtype, no_defer);
if (!no_defer && 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) if (seq_ret == FT_RRB_SEQ_DROP)
goto out; goto out;
@ -2346,6 +2671,13 @@ static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth,
&plain, &plain_len) < 0) &plain, &plain_len) < 0)
goto out; 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) { if (seq_ret == FT_RRB_SEQ_DEFER) {
wpa_ft_rrb_seq_req(wpa_auth, r1kh->seq, src_addr, f_r0kh_id, wpa_ft_rrb_seq_req(wpa_auth, r1kh->seq, src_addr, f_r0kh_id,
f_r0kh_id_len, f_r1kh_id, key, key_len, f_r0kh_id_len, f_r1kh_id, key, key_len,
@ -2356,6 +2688,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, wpa_ft_rrb_seq_accept(wpa_auth, r1kh->seq, src_addr, auth, auth_len,
msgtype); 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); 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, wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name", f_pmk_r0_name,
@ -2392,10 +2726,8 @@ static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth,
resp_auth[4].len = 0; resp_auth[4].len = 0;
resp_auth[4].data = NULL; resp_auth[4].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"); 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, ret = wpa_ft_rrb_build_r0(key, key_len, resp, r0, f_r1kh_id, f_s1kh_id,
resp_auth, wpa_auth->addr, resp_auth, wpa_auth->addr,
@ -2417,6 +2749,7 @@ out:
/* @returns 0 on success /* @returns 0 on success
* -1 on error * -1 on error
* -2 if FR_RRB_PAIRWISE is missing
*/ */
static int wpa_ft_rrb_rx_r1(struct wpa_authenticator *wpa_auth, static int wpa_ft_rrb_rx_r1(struct wpa_authenticator *wpa_auth,
const u8 *src_addr, u8 type, const u8 *src_addr, u8 type,
@ -2431,7 +2764,7 @@ static int wpa_ft_rrb_rx_r1(struct wpa_authenticator *wpa_auth,
{ {
u8 *plain = NULL; u8 *plain = NULL;
size_t plain_len = 0; size_t plain_len = 0;
struct ft_remote_r0kh *r0kh; struct ft_remote_r0kh *r0kh, *r0kh_wildcard;
const u8 *key; const u8 *key;
size_t key_len; size_t key_len;
int seq_ret; int seq_ret;
@ -2453,14 +2786,31 @@ static int wpa_ft_rrb_rx_r1(struct wpa_authenticator *wpa_auth,
goto out; goto out;
} }
wpa_ft_rrb_lookup_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len, &r0kh); wpa_ft_rrb_lookup_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len, &r0kh,
if (!r0kh || os_memcmp(r0kh->addr, src_addr, ETH_ALEN) != 0) &r0kh_wildcard);
goto out; if (r0kh) {
key = r0kh->key; key = r0kh->key;
key_len = sizeof(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;
}
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;
}
seq_ret = wpa_ft_rrb_seq_chk(r0kh->seq, src_addr, enc, enc_len, auth,
auth_len, msgtype, cb ? 0 : 1);
if (seq_ret == FT_RRB_SEQ_DROP) if (seq_ret == FT_RRB_SEQ_DROP)
goto out; goto out;
@ -2468,6 +2818,13 @@ static int wpa_ft_rrb_rx_r1(struct wpa_authenticator *wpa_auth,
src_addr, type, &plain, &plain_len) < 0) src_addr, type, &plain, &plain_len) < 0)
goto out; 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) { if (seq_ret == FT_RRB_SEQ_DEFER) {
wpa_ft_rrb_seq_req(wpa_auth, r0kh->seq, src_addr, f_r0kh_id, wpa_ft_rrb_seq_req(wpa_auth, r0kh->seq, src_addr, f_r0kh_id,
f_r0kh_id_len, f_r1kh_id, key, key_len, f_r0kh_id_len, f_r1kh_id, key, key_len,
@ -2477,6 +2834,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, wpa_ft_rrb_seq_accept(wpa_auth, r0kh->seq, src_addr, auth, auth_len,
msgtype); 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); RRB_GET(FT_RRB_S1KH_ID, s1kh_id, msgtype, ETH_ALEN);
wpa_printf(MSG_DEBUG, "FT: S1KH-ID=" MACSTR, MAC2STR(f_s1kh_id)); wpa_printf(MSG_DEBUG, "FT: S1KH-ID=" MACSTR, MAC2STR(f_s1kh_id));
@ -2484,9 +2843,11 @@ static int wpa_ft_rrb_rx_r1(struct wpa_authenticator *wpa_auth,
if (s1kh_id_out) if (s1kh_id_out)
os_memcpy(s1kh_id_out, f_s1kh_id, ETH_ALEN); os_memcpy(s1kh_id_out, f_s1kh_id, ETH_ALEN);
ret = -2;
RRB_GET(FT_RRB_PAIRWISE, pairwise, msgtype, sizeof(le16)); RRB_GET(FT_RRB_PAIRWISE, pairwise, msgtype, sizeof(le16));
wpa_hexdump(MSG_DEBUG, "FT: pairwise", f_pairwise, f_pairwise_len); 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); RRB_GET(FT_RRB_PMK_R1_NAME, pmk_r1_name, msgtype, WPA_PMK_NAME_LEN);
wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name",
f_pmk_r1_name, WPA_PMK_NAME_LEN); f_pmk_r1_name, WPA_PMK_NAME_LEN);
@ -2519,13 +2880,20 @@ static void ft_finish_pull(struct wpa_state_machine *sm)
size_t resp_ies_len; size_t resp_ies_len;
u16 status; u16 status;
if (!sm->ft_pending_cb || !sm->ft_pending_req_ies)
return;
res = wpa_ft_process_auth_req(sm, wpabuf_head(sm->ft_pending_req_ies), res = wpa_ft_process_auth_req(sm, wpabuf_head(sm->ft_pending_req_ies),
wpabuf_len(sm->ft_pending_req_ies), wpabuf_len(sm->ft_pending_req_ies),
&resp_ies, &resp_ies_len); &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); wpabuf_free(sm->ft_pending_req_ies);
sm->ft_pending_req_ies = NULL; sm->ft_pending_req_ies = NULL;
if (res < 0)
res = WLAN_STATUS_UNSPECIFIED_FAILURE;
status = res; status = res;
wpa_printf(MSG_DEBUG, "FT: Postponed auth callback result for " MACSTR wpa_printf(MSG_DEBUG, "FT: Postponed auth callback result for " MACSTR
" - status %u", MAC2STR(sm->addr), status); " - status %u", MAC2STR(sm->addr), status);
@ -2568,7 +2936,7 @@ static int wpa_ft_rrb_rx_resp(struct wpa_authenticator *wpa_auth,
int no_defer) int no_defer)
{ {
const char *msgtype = "pull response"; const char *msgtype = "pull response";
int ret = -1; int nak, ret = -1;
struct ft_get_sta_ctx ctx; struct ft_get_sta_ctx ctx;
u8 s1kh_id[ETH_ALEN]; u8 s1kh_id[ETH_ALEN];
const u8 *f_nonce; const u8 *f_nonce;
@ -2590,6 +2958,12 @@ static int wpa_ft_rrb_rx_resp(struct wpa_authenticator *wpa_auth,
ret = wpa_ft_rrb_rx_r1(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_RESP, ret = wpa_ft_rrb_rx_r1(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_RESP,
enc, enc_len, auth, auth_len, msgtype, s1kh_id, enc, enc_len, auth, auth_len, msgtype, s1kh_id,
no_defer ? NULL : &wpa_ft_rrb_rx_resp); no_defer ? NULL : &wpa_ft_rrb_rx_resp);
if (ret == -2) {
ret = 0;
nak = 1;
} else {
nak = 0;
}
if (ret < 0) if (ret < 0)
return -1; return -1;
@ -2598,6 +2972,9 @@ static int wpa_ft_rrb_rx_resp(struct wpa_authenticator *wpa_auth,
wpa_printf(MSG_DEBUG, wpa_printf(MSG_DEBUG,
"FT: Response to a pending pull request for " MACSTR, "FT: Response to a pending pull request for " MACSTR,
MAC2STR(ctx.sm->addr)); 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); ft_finish_pull(ctx.sm);
} }
@ -2629,7 +3006,11 @@ static int wpa_ft_rrb_rx_seq(struct wpa_authenticator *wpa_auth,
const u8 *enc, size_t enc_len, const u8 *enc, size_t enc_len,
const u8 *auth, size_t auth_len, const u8 *auth, size_t auth_len,
struct ft_remote_seq **rkh_seq, 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_r0kh *r0kh = NULL;
struct ft_remote_r1kh *r1kh = NULL; struct ft_remote_r1kh *r1kh = NULL;
@ -2638,6 +3019,8 @@ static int wpa_ft_rrb_rx_seq(struct wpa_authenticator *wpa_auth,
int to_r0kh, to_r1kh; int to_r0kh, to_r1kh;
u8 *plain = NULL; u8 *plain = NULL;
size_t plain_len = 0; 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_R0KH_ID, r0kh_id, "seq", -1);
RRB_GET_AUTH(FT_RRB_R1KH_ID, r1kh_id, "seq", FT_R1KH_ID_LEN); RRB_GET_AUTH(FT_RRB_R1KH_ID, r1kh_id, "seq", FT_R1KH_ID_LEN);
@ -2657,29 +3040,38 @@ static int wpa_ft_rrb_rx_seq(struct wpa_authenticator *wpa_auth,
if (!to_r0kh) { if (!to_r0kh) {
wpa_ft_rrb_lookup_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len, wpa_ft_rrb_lookup_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len,
&r0kh); &r0kh, &r0kh_wildcard);
if (!r0kh || os_memcmp(r0kh->addr, src_addr, ETH_ALEN) != 0) { if (!r0kh_wildcard &&
(!r0kh || os_memcmp(r0kh->addr, src_addr, ETH_ALEN) != 0)) {
wpa_hexdump(MSG_DEBUG, "FT: Did not find R0KH-ID", wpa_hexdump(MSG_DEBUG, "FT: Did not find R0KH-ID",
f_r0kh_id, f_r0kh_id_len); f_r0kh_id, f_r0kh_id_len);
goto out; goto out;
} }
if (r0kh) {
*key = r0kh->key; *key = r0kh->key;
*key_len = sizeof(r0kh->key); *key_len = sizeof(r0kh->key);
*rkh_seq = r0kh->seq; } else {
*key = r0kh_wildcard->key;
*key_len = sizeof(r0kh_wildcard->key);
}
} }
if (!to_r1kh) { if (!to_r1kh) {
wpa_ft_rrb_lookup_r1kh(wpa_auth, f_r1kh_id, &r1kh); wpa_ft_rrb_lookup_r1kh(wpa_auth, f_r1kh_id, &r1kh,
if (!r1kh || os_memcmp(r1kh->addr, src_addr, ETH_ALEN) != 0) { &r1kh_wildcard);
if (!r1kh_wildcard &&
(!r1kh || os_memcmp(r1kh->addr, src_addr, ETH_ALEN) != 0)) {
wpa_hexdump(MSG_DEBUG, "FT: Did not find R1KH-ID", wpa_hexdump(MSG_DEBUG, "FT: Did not find R1KH-ID",
f_r1kh_id, FT_R1KH_ID_LEN); f_r1kh_id, FT_R1KH_ID_LEN);
goto out; goto out;
} }
if (r1kh) {
*key = r1kh->key; *key = r1kh->key;
*key_len = sizeof(r1kh->key); *key_len = sizeof(r1kh->key);
*rkh_seq = r1kh->seq; } else {
*key = r1kh_wildcard->key;
*key_len = sizeof(r1kh_wildcard->key);
}
} }
if (wpa_ft_rrb_decrypt(*key, *key_len, enc, enc_len, auth, auth_len, if (wpa_ft_rrb_decrypt(*key, *key_len, enc, enc_len, auth, auth_len,
@ -2688,6 +3080,39 @@ static int wpa_ft_rrb_rx_seq(struct wpa_authenticator *wpa_auth,
os_free(plain); os_free(plain);
if (!to_r0kh) {
if (!r0kh)
r0kh = wpa_ft_rrb_add_r0kh(wpa_auth, r0kh_wildcard,
src_addr, f_r0kh_id,
f_r0kh_id_len,
ftRRBseqTimeout);
if (!r0kh)
goto out;
wpa_ft_rrb_r0kh_replenish(wpa_auth, r0kh, ftRRBseqTimeout);
*rkh_seq = r0kh->seq;
if (r0kh_out)
*r0kh_out = r0kh;
if (r0kh_wildcard_out)
*r0kh_wildcard_out = r0kh_wildcard;
}
if (!to_r1kh) {
if (!r1kh)
r1kh = wpa_ft_rrb_add_r1kh(wpa_auth, r1kh_wildcard,
src_addr, f_r1kh_id,
ftRRBseqTimeout);
if (!r1kh)
goto out;
wpa_ft_rrb_r1kh_replenish(wpa_auth, r1kh, ftRRBseqTimeout);
*rkh_seq = r1kh->seq;
if (r1kh_out)
*r1kh_out = r1kh;
if (r1kh_wildcard_out)
*r1kh_wildcard_out = r1kh_wildcard;
}
return 0; return 0;
out: out:
return -1; return -1;
@ -2704,7 +3129,7 @@ static int wpa_ft_rrb_rx_seq_req(struct wpa_authenticator *wpa_auth,
struct ft_rrb_seq f_seq; struct ft_rrb_seq f_seq;
const u8 *f_nonce, *f_r0kh_id, *f_r1kh_id; const u8 *f_nonce, *f_r0kh_id, *f_r1kh_id;
size_t f_nonce_len, f_r0kh_id_len, f_r1kh_id_len; 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; u8 *packet = NULL, *key = NULL;
size_t packet_len = 0, key_len = 0; size_t packet_len = 0, key_len = 0;
struct tlv_list seq_resp_auth[5]; struct tlv_list seq_resp_auth[5];
@ -2713,7 +3138,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, 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, enc, enc_len, auth, auth_len, &rkh_seq, &key,
&key_len) < 0) &key_len, NULL, NULL, NULL, NULL) < 0)
goto out; goto out;
RRB_GET_AUTH(FT_RRB_NONCE, nonce, "seq request", FT_RRB_NONCE_LEN); RRB_GET_AUTH(FT_RRB_NONCE, nonce, "seq request", FT_RRB_NONCE_LEN);
@ -2767,6 +3192,8 @@ static int wpa_ft_rrb_rx_seq_resp(struct wpa_authenticator *wpa_auth,
{ {
u8 *key = NULL; u8 *key = NULL;
size_t key_len = 0; 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; const u8 *f_nonce, *f_seq;
size_t f_nonce_len, f_seq_len; size_t f_nonce_len, f_seq_len;
struct ft_remote_seq *rkh_seq = NULL; struct ft_remote_seq *rkh_seq = NULL;
@ -2780,7 +3207,8 @@ 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, 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, enc, enc_len, auth, auth_len, &rkh_seq, &key,
&key_len) < 0) &key_len, &r0kh, &r1kh, &r0kh_wildcard,
&r1kh_wildcard) < 0)
goto out; goto out;
RRB_GET_AUTH(FT_RRB_NONCE, nonce, "seq response", FT_RRB_NONCE_LEN); RRB_GET_AUTH(FT_RRB_NONCE, nonce, "seq response", FT_RRB_NONCE_LEN);
@ -2804,6 +3232,20 @@ static int wpa_ft_rrb_rx_seq_resp(struct wpa_authenticator *wpa_auth,
goto out; 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, seq_ret = wpa_ft_rrb_seq_chk(rkh_seq, src_addr, enc, enc_len, auth,
auth_len, "seq response", 1); auth_len, "seq response", 1);
if (seq_ret == FT_RRB_SEQ_OK) { if (seq_ret == FT_RRB_SEQ_OK) {
@ -2967,6 +3409,7 @@ void wpa_ft_rrb_oui_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr,
{ {
const u8 *auth, *enc; const u8 *auth, *enc;
size_t alen, elen; size_t alen, elen;
int no_defer = 0;
wpa_printf(MSG_DEBUG, "FT: RRB-OUI received frame from remote AP " wpa_printf(MSG_DEBUG, "FT: RRB-OUI received frame from remote AP "
MACSTR, MAC2STR(src_addr)); MACSTR, MAC2STR(src_addr));
@ -2984,7 +3427,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 "FT: RRB-OUI received frame from remote AP " MACSTR
" to multicast address " MACSTR, " to multicast address " MACSTR,
MAC2STR(src_addr), MAC2STR(dst_addr)); MAC2STR(src_addr), MAC2STR(dst_addr));
return; no_defer = 1;
} }
if (data_len < sizeof(u16)) { if (data_len < sizeof(u16)) {
@ -3005,23 +3448,23 @@ void wpa_ft_rrb_oui_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr,
switch (oui_suffix) { switch (oui_suffix) {
case FT_PACKET_R0KH_R1KH_PULL: case FT_PACKET_R0KH_R1KH_PULL:
wpa_ft_rrb_rx_pull(wpa_auth, src_addr, enc, elen, auth, alen, wpa_ft_rrb_rx_pull(wpa_auth, src_addr, enc, elen, auth, alen,
0); no_defer);
break; break;
case FT_PACKET_R0KH_R1KH_RESP: case FT_PACKET_R0KH_R1KH_RESP:
wpa_ft_rrb_rx_resp(wpa_auth, src_addr, enc, elen, auth, alen, wpa_ft_rrb_rx_resp(wpa_auth, src_addr, enc, elen, auth, alen,
0); no_defer);
break; break;
case FT_PACKET_R0KH_R1KH_PUSH: case FT_PACKET_R0KH_R1KH_PUSH:
wpa_ft_rrb_rx_push(wpa_auth, src_addr, enc, elen, auth, alen, wpa_ft_rrb_rx_push(wpa_auth, src_addr, enc, elen, auth, alen,
0); no_defer);
break; break;
case FT_PACKET_R0KH_R1KH_SEQ_REQ: case FT_PACKET_R0KH_R1KH_SEQ_REQ:
wpa_ft_rrb_rx_seq_req(wpa_auth, src_addr, enc, elen, auth, alen, wpa_ft_rrb_rx_seq_req(wpa_auth, src_addr, enc, elen, auth, alen,
0); no_defer);
break; break;
case FT_PACKET_R0KH_R1KH_SEQ_RESP: case FT_PACKET_R0KH_R1KH_SEQ_RESP:
wpa_ft_rrb_rx_seq_resp(wpa_auth, src_addr, enc, elen, auth, wpa_ft_rrb_rx_seq_resp(wpa_auth, src_addr, enc, elen, auth,
alen, 0); alen, no_defer);
break; break;
} }
} }
@ -3079,6 +3522,8 @@ void wpa_ft_push_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *addr)
if (!wpa_auth->conf.pmk_r1_push) if (!wpa_auth->conf.pmk_r1_push)
return; return;
if (!wpa_auth->conf.r1kh_list)
return;
r0 = wpa_auth->ft_pmk_cache->pmk_r0; r0 = wpa_auth->ft_pmk_cache->pmk_r0;
while (r0) { while (r0) {
@ -3094,7 +3539,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 " wpa_printf(MSG_DEBUG, "FT: Deriving and pushing PMK-R1 keys to R1KHs "
"for STA " MACSTR, MAC2STR(addr)); "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) if (wpa_ft_rrb_init_r1kh_seq(r1kh) < 0)
continue; continue;
wpa_ft_generate_pmk_r1(wpa_auth, r0, r1kh, addr); wpa_ft_generate_pmk_r1(wpa_auth, r0, r1kh, addr);

View file

@ -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); os_memcpy(wconf->r1_key_holder, conf->r1_key_holder, FT_R1KH_ID_LEN);
wconf->r0_key_lifetime = conf->r0_key_lifetime; wconf->r0_key_lifetime = conf->r0_key_lifetime;
wconf->reassociation_deadline = conf->reassociation_deadline; wconf->reassociation_deadline = conf->reassociation_deadline;
wconf->r0kh_list = conf->r0kh_list; wconf->rkh_pos_timeout = conf->rkh_pos_timeout;
wconf->r1kh_list = conf->r1kh_list; 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->pmk_r1_push = conf->pmk_r1_push;
wconf->ft_over_ds = conf->ft_over_ds; wconf->ft_over_ds = conf->ft_over_ds;
wconf->ft_psk_generate_local = conf->ft_psk_generate_local; wconf->ft_psk_generate_local = conf->ft_psk_generate_local;

View file

@ -126,6 +126,7 @@ struct wpa_state_machine {
u8 ft_pending_pull_nonce[FT_RRB_NONCE_LEN]; u8 ft_pending_pull_nonce[FT_RRB_NONCE_LEN];
u8 ft_pending_auth_transaction; u8 ft_pending_auth_transaction;
u8 ft_pending_current_ap[ETH_ALEN]; u8 ft_pending_current_ap[ETH_ALEN];
int ft_pending_pull_left_retries;
#endif /* CONFIG_IEEE80211R_AP */ #endif /* CONFIG_IEEE80211R_AP */
int pending_1_of_4_timeout; int pending_1_of_4_timeout;