FT: Add support for postponing FT response
If the PMK-R1 needs to be pulled for the R0KH, the previous implementation ended up rejecting the over-the-air authentication and over-the-DS action frame unnecessarily while waiting for the RRB response. Improve this by postponing the Authentication/Action frame response until the pull response is received. Signed-off-by: Jouni Malinen <j@w1.fi>
This commit is contained in:
parent
546735476a
commit
692ec3058b
4 changed files with 154 additions and 47 deletions
|
@ -621,6 +621,7 @@ static void wpa_free_sta_sm(struct wpa_state_machine *sm)
|
|||
}
|
||||
#ifdef CONFIG_IEEE80211R
|
||||
os_free(sm->assoc_resp_ftie);
|
||||
wpabuf_free(sm->ft_pending_req_ies);
|
||||
#endif /* CONFIG_IEEE80211R */
|
||||
os_free(sm->last_rx_eapol_key);
|
||||
os_free(sm->wpa_ie);
|
||||
|
|
|
@ -42,6 +42,7 @@ struct ft_rrb_frame {
|
|||
#define FT_R0KH_R1KH_PULL_DATA_LEN 44
|
||||
#define FT_R0KH_R1KH_RESP_DATA_LEN 76
|
||||
#define FT_R0KH_R1KH_PUSH_DATA_LEN 88
|
||||
#define FT_R0KH_R1KH_PULL_NONCE_LEN 16
|
||||
|
||||
struct ft_r0kh_r1kh_pull_frame {
|
||||
u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */
|
||||
|
@ -49,7 +50,7 @@ struct ft_r0kh_r1kh_pull_frame {
|
|||
le16 data_length; /* little endian length of data (44) */
|
||||
u8 ap_address[ETH_ALEN];
|
||||
|
||||
u8 nonce[16];
|
||||
u8 nonce[FT_R0KH_R1KH_PULL_NONCE_LEN];
|
||||
u8 pmk_r0_name[WPA_PMK_NAME_LEN];
|
||||
u8 r1kh_id[FT_R1KH_ID_LEN];
|
||||
u8 s1kh_id[ETH_ALEN];
|
||||
|
@ -63,7 +64,7 @@ struct ft_r0kh_r1kh_resp_frame {
|
|||
le16 data_length; /* little endian length of data (76) */
|
||||
u8 ap_address[ETH_ALEN];
|
||||
|
||||
u8 nonce[16]; /* copied from pull */
|
||||
u8 nonce[FT_R0KH_R1KH_PULL_NONCE_LEN]; /* copied from pull */
|
||||
u8 r1kh_id[FT_R1KH_ID_LEN]; /* copied from pull */
|
||||
u8 s1kh_id[ETH_ALEN]; /* copied from pull */
|
||||
u8 pmk_r1[PMK_LEN];
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* hostapd - IEEE 802.11r - Fast BSS Transition
|
||||
* Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
|
||||
* Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
|
@ -9,6 +9,7 @@
|
|||
#include "utils/includes.h"
|
||||
|
||||
#include "utils/common.h"
|
||||
#include "utils/eloop.h"
|
||||
#include "common/ieee802_11_defs.h"
|
||||
#include "common/ieee802_11_common.h"
|
||||
#include "crypto/aes_wrap.h"
|
||||
|
@ -22,6 +23,12 @@
|
|||
|
||||
#ifdef CONFIG_IEEE80211R
|
||||
|
||||
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 int wpa_ft_rrb_send(struct wpa_authenticator *wpa_auth, const u8 *dst,
|
||||
const u8 *data, size_t data_len)
|
||||
{
|
||||
|
@ -293,22 +300,25 @@ static int wpa_ft_fetch_pmk_r1(struct wpa_authenticator *wpa_auth,
|
|||
}
|
||||
|
||||
|
||||
static int wpa_ft_pull_pmk_r1(struct wpa_authenticator *wpa_auth,
|
||||
const u8 *s1kh_id, const u8 *r0kh_id,
|
||||
size_t r0kh_id_len, const u8 *pmk_r0_name)
|
||||
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_r0kh_r1kh_pull_frame frame, f;
|
||||
|
||||
r0kh = wpa_auth->conf.r0kh_list;
|
||||
r0kh = sm->wpa_auth->conf.r0kh_list;
|
||||
while (r0kh) {
|
||||
if (r0kh->id_len == r0kh_id_len &&
|
||||
os_memcmp(r0kh->id, r0kh_id, r0kh_id_len) == 0)
|
||||
if (r0kh->id_len == sm->r0kh_id_len &&
|
||||
os_memcmp(r0kh->id, sm->r0kh_id, sm->r0kh_id_len) == 0)
|
||||
break;
|
||||
r0kh = r0kh->next;
|
||||
}
|
||||
if (r0kh == NULL)
|
||||
if (r0kh == NULL) {
|
||||
wpa_hexdump(MSG_DEBUG, "FT: Did not find R0KH-ID",
|
||||
sm->r0kh_id, sm->r0kh_id_len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
wpa_printf(MSG_DEBUG, "FT: Send PMK-R1 pull request to remote R0KH "
|
||||
"address " MACSTR, MAC2STR(r0kh->addr));
|
||||
|
@ -317,25 +327,32 @@ static int wpa_ft_pull_pmk_r1(struct wpa_authenticator *wpa_auth,
|
|||
frame.frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB;
|
||||
frame.packet_type = FT_PACKET_R0KH_R1KH_PULL;
|
||||
frame.data_length = host_to_le16(FT_R0KH_R1KH_PULL_DATA_LEN);
|
||||
os_memcpy(frame.ap_address, wpa_auth->addr, ETH_ALEN);
|
||||
os_memcpy(frame.ap_address, sm->wpa_auth->addr, ETH_ALEN);
|
||||
|
||||
/* aes_wrap() does not support inplace encryption, so use a temporary
|
||||
* buffer for the data. */
|
||||
if (random_get_bytes(f.nonce, sizeof(f.nonce))) {
|
||||
if (random_get_bytes(f.nonce, FT_R0KH_R1KH_PULL_NONCE_LEN)) {
|
||||
wpa_printf(MSG_DEBUG, "FT: Failed to get random data for "
|
||||
"nonce");
|
||||
return -1;
|
||||
}
|
||||
os_memcpy(sm->ft_pending_pull_nonce, f.nonce,
|
||||
FT_R0KH_R1KH_PULL_NONCE_LEN);
|
||||
os_memcpy(f.pmk_r0_name, pmk_r0_name, WPA_PMK_NAME_LEN);
|
||||
os_memcpy(f.r1kh_id, wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN);
|
||||
os_memcpy(f.s1kh_id, s1kh_id, ETH_ALEN);
|
||||
os_memcpy(f.r1kh_id, sm->wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN);
|
||||
os_memcpy(f.s1kh_id, sm->addr, ETH_ALEN);
|
||||
os_memset(f.pad, 0, sizeof(f.pad));
|
||||
|
||||
if (aes_wrap(r0kh->key, (FT_R0KH_R1KH_PULL_DATA_LEN + 7) / 8,
|
||||
f.nonce, frame.nonce) < 0)
|
||||
return -1;
|
||||
|
||||
wpa_ft_rrb_send(wpa_auth, r0kh->addr, (u8 *) &frame, sizeof(frame));
|
||||
wpabuf_free(sm->ft_pending_req_ies);
|
||||
sm->ft_pending_req_ies = wpabuf_alloc_copy(ies, ies_len);
|
||||
if (sm->ft_pending_req_ies == NULL)
|
||||
return -1;
|
||||
|
||||
wpa_ft_rrb_send(sm->wpa_auth, r0kh->addr, (u8 *) &frame, sizeof(frame));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -777,7 +794,7 @@ void wpa_ft_install_ptk(struct wpa_state_machine *sm)
|
|||
}
|
||||
|
||||
|
||||
static u16 wpa_ft_process_auth_req(struct wpa_state_machine *sm,
|
||||
static int wpa_ft_process_auth_req(struct wpa_state_machine *sm,
|
||||
const u8 *ies, size_t ies_len,
|
||||
u8 **resp_ies, size_t *resp_ies_len)
|
||||
{
|
||||
|
@ -848,19 +865,13 @@ static u16 wpa_ft_process_auth_req(struct wpa_state_machine *sm,
|
|||
|
||||
if (wpa_ft_fetch_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1_name, pmk_r1,
|
||||
&pairwise) < 0) {
|
||||
if (wpa_ft_pull_pmk_r1(sm->wpa_auth, sm->addr, sm->r0kh_id,
|
||||
sm->r0kh_id_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 "
|
||||
"PMK-R1 and unknown R0KH-ID");
|
||||
return WLAN_STATUS_INVALID_PMKID;
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: Should return "status pending" (and the caller should
|
||||
* not send out response now). The real response will be sent
|
||||
* once the response from R0KH is received.
|
||||
*/
|
||||
return WLAN_STATUS_INVALID_PMKID;
|
||||
return -1; /* Status pending */
|
||||
}
|
||||
|
||||
wpa_hexdump_key(MSG_DEBUG, "FT: Selected PMK-R1", pmk_r1, PMK_LEN);
|
||||
|
@ -940,6 +951,7 @@ void wpa_ft_process_auth(struct wpa_state_machine *sm, const u8 *bssid,
|
|||
u16 status;
|
||||
u8 *resp_ies;
|
||||
size_t resp_ies_len;
|
||||
int res;
|
||||
|
||||
if (sm == NULL) {
|
||||
wpa_printf(MSG_DEBUG, "FT: Received authentication frame, but "
|
||||
|
@ -950,8 +962,16 @@ void wpa_ft_process_auth(struct wpa_state_machine *sm, const u8 *bssid,
|
|||
wpa_printf(MSG_DEBUG, "FT: Received authentication frame: STA=" MACSTR
|
||||
" BSSID=" MACSTR " transaction=%d",
|
||||
MAC2STR(sm->addr), MAC2STR(bssid), auth_transaction);
|
||||
status = wpa_ft_process_auth_req(sm, ies, ies_len, &resp_ies,
|
||||
&resp_ies_len);
|
||||
sm->ft_pending_cb = cb;
|
||||
sm->ft_pending_cb_ctx = ctx;
|
||||
sm->ft_pending_auth_transaction = auth_transaction;
|
||||
res = wpa_ft_process_auth_req(sm, ies, ies_len, &resp_ies,
|
||||
&resp_ies_len);
|
||||
if (res < 0) {
|
||||
wpa_printf(MSG_DEBUG, "FT: Callback postponed until response is available");
|
||||
return;
|
||||
}
|
||||
status = res;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "FT: FT authentication response: dst=" MACSTR
|
||||
" auth_transaction=%d status=%d",
|
||||
|
@ -1182,15 +1202,27 @@ int wpa_ft_action_rx(struct wpa_state_machine *sm, const u8 *data, size_t len)
|
|||
}
|
||||
|
||||
|
||||
static void wpa_ft_rrb_rx_request_cb(void *ctx, const u8 *dst, const u8 *bssid,
|
||||
u16 auth_transaction, u16 resp,
|
||||
const u8 *ies, size_t ies_len)
|
||||
{
|
||||
struct wpa_state_machine *sm = ctx;
|
||||
wpa_printf(MSG_DEBUG, "FT: Over-the-DS RX request cb for " MACSTR,
|
||||
MAC2STR(sm->addr));
|
||||
wpa_ft_send_rrb_auth_resp(sm, sm->ft_pending_current_ap, sm->addr,
|
||||
WLAN_STATUS_SUCCESS, ies, ies_len);
|
||||
}
|
||||
|
||||
|
||||
static int wpa_ft_rrb_rx_request(struct wpa_authenticator *wpa_auth,
|
||||
const u8 *current_ap, const u8 *sta_addr,
|
||||
const u8 *body, size_t len)
|
||||
{
|
||||
struct wpa_state_machine *sm;
|
||||
u16 status;
|
||||
u8 *resp_ies, *pos;
|
||||
size_t resp_ies_len, rlen;
|
||||
struct ft_rrb_frame *frame;
|
||||
u8 *resp_ies;
|
||||
size_t resp_ies_len;
|
||||
int res;
|
||||
|
||||
sm = wpa_ft_add_sta(wpa_auth, sta_addr);
|
||||
if (sm == NULL) {
|
||||
|
@ -1201,8 +1233,33 @@ static int wpa_ft_rrb_rx_request(struct wpa_authenticator *wpa_auth,
|
|||
|
||||
wpa_hexdump(MSG_MSGDUMP, "FT: RRB Request Frame body", body, len);
|
||||
|
||||
status = wpa_ft_process_auth_req(sm, body, len, &resp_ies,
|
||||
&resp_ies_len);
|
||||
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);
|
||||
res = wpa_ft_process_auth_req(sm, body, len, &resp_ies,
|
||||
&resp_ies_len);
|
||||
if (res < 0) {
|
||||
wpa_printf(MSG_DEBUG, "FT: No immediate response available - wait for pull response");
|
||||
return 0;
|
||||
}
|
||||
status = res;
|
||||
|
||||
res = wpa_ft_send_rrb_auth_resp(sm, current_ap, sta_addr, status,
|
||||
resp_ies, resp_ies_len);
|
||||
os_free(resp_ies);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
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)
|
||||
{
|
||||
struct wpa_authenticator *wpa_auth = sm->wpa_auth;
|
||||
size_t rlen;
|
||||
struct ft_rrb_frame *frame;
|
||||
u8 *pos;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "FT: RRB authentication response: STA=" MACSTR
|
||||
" CurrentAP=" MACSTR " status=%d",
|
||||
|
@ -1218,10 +1275,8 @@ static int wpa_ft_rrb_rx_request(struct wpa_authenticator *wpa_auth,
|
|||
rlen = 2 + 2 * ETH_ALEN + 2 + resp_ies_len;
|
||||
|
||||
frame = os_malloc(sizeof(*frame) + rlen);
|
||||
if (frame == NULL) {
|
||||
os_free(resp_ies);
|
||||
if (frame == NULL)
|
||||
return -1;
|
||||
}
|
||||
frame->frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB;
|
||||
frame->packet_type = FT_PACKET_RESPONSE;
|
||||
frame->action_length = host_to_le16(rlen);
|
||||
|
@ -1235,10 +1290,8 @@ static int wpa_ft_rrb_rx_request(struct wpa_authenticator *wpa_auth,
|
|||
pos += ETH_ALEN;
|
||||
WPA_PUT_LE16(pos, status);
|
||||
pos += 2;
|
||||
if (resp_ies) {
|
||||
if (resp_ies)
|
||||
os_memcpy(pos, resp_ies, resp_ies_len);
|
||||
os_free(resp_ies);
|
||||
}
|
||||
|
||||
wpa_ft_rrb_send(wpa_auth, current_ap, (u8 *) frame,
|
||||
sizeof(*frame) + rlen);
|
||||
|
@ -1290,7 +1343,7 @@ static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth,
|
|||
f.nonce, sizeof(f.nonce));
|
||||
wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - PMKR0Name",
|
||||
f.pmk_r0_name, WPA_PMK_NAME_LEN);
|
||||
wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull - R1KH-ID=" MACSTR "S1KH-ID="
|
||||
wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull - R1KH-ID=" MACSTR " S1KH-ID="
|
||||
MACSTR, MAC2STR(f.r1kh_id), MAC2STR(f.s1kh_id));
|
||||
|
||||
os_memset(&resp, 0, sizeof(resp));
|
||||
|
@ -1333,13 +1386,58 @@ static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth,
|
|||
}
|
||||
|
||||
|
||||
static void ft_pull_resp_cb_finish(void *eloop_ctx, void *timeout_ctx)
|
||||
{
|
||||
struct wpa_state_machine *sm = eloop_ctx;
|
||||
int res;
|
||||
u8 *resp_ies;
|
||||
size_t resp_ies_len;
|
||||
u16 status;
|
||||
|
||||
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);
|
||||
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);
|
||||
|
||||
sm->ft_pending_cb(sm->ft_pending_cb_ctx, sm->addr, sm->wpa_auth->addr,
|
||||
sm->ft_pending_auth_transaction + 1, status,
|
||||
resp_ies, resp_ies_len);
|
||||
os_free(resp_ies);
|
||||
}
|
||||
|
||||
|
||||
static int ft_pull_resp_cb(struct wpa_state_machine *sm, void *ctx)
|
||||
{
|
||||
struct ft_r0kh_r1kh_resp_frame *frame = ctx;
|
||||
|
||||
if (os_memcmp(frame->s1kh_id, sm->addr, ETH_ALEN) != 0)
|
||||
return 0;
|
||||
if (os_memcmp(frame->nonce, sm->ft_pending_pull_nonce,
|
||||
FT_R0KH_R1KH_PULL_NONCE_LEN) != 0)
|
||||
return 0;
|
||||
if (sm->ft_pending_cb == NULL || sm->ft_pending_req_ies == NULL)
|
||||
return 0;
|
||||
|
||||
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);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int wpa_ft_rrb_rx_resp(struct wpa_authenticator *wpa_auth,
|
||||
const u8 *src_addr,
|
||||
const u8 *data, size_t data_len)
|
||||
{
|
||||
struct ft_r0kh_r1kh_resp_frame *frame, f;
|
||||
struct ft_remote_r0kh *r0kh;
|
||||
int pairwise;
|
||||
int pairwise, res;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 pull response");
|
||||
|
||||
|
@ -1376,14 +1474,10 @@ static int wpa_ft_rrb_rx_resp(struct wpa_authenticator *wpa_auth,
|
|||
return -1;
|
||||
}
|
||||
|
||||
/* TODO: verify that <nonce,s1kh_id> matches with a pending request
|
||||
* and call this requests callback function to finish request
|
||||
* processing */
|
||||
|
||||
pairwise = le_to_host16(f.pairwise);
|
||||
wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - nonce",
|
||||
f.nonce, sizeof(f.nonce));
|
||||
wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull - R1KH-ID=" MACSTR "S1KH-ID="
|
||||
wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull - R1KH-ID=" MACSTR " S1KH-ID="
|
||||
MACSTR " pairwise=0x%x",
|
||||
MAC2STR(f.r1kh_id), MAC2STR(f.s1kh_id), pairwise);
|
||||
wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1 pull - PMK-R1",
|
||||
|
@ -1391,11 +1485,13 @@ static int wpa_ft_rrb_rx_resp(struct wpa_authenticator *wpa_auth,
|
|||
wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - PMKR1Name",
|
||||
f.pmk_r1_name, WPA_PMK_NAME_LEN);
|
||||
|
||||
wpa_ft_store_pmk_r1(wpa_auth, f.s1kh_id, f.pmk_r1, f.pmk_r1_name,
|
||||
pairwise);
|
||||
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);
|
||||
os_memset(f.pmk_r1, 0, PMK_LEN);
|
||||
|
||||
return 0;
|
||||
return res ? 0 : -1;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -118,6 +118,15 @@ struct wpa_state_machine {
|
|||
u8 sup_pmk_r1_name[WPA_PMK_NAME_LEN]; /* PMKR1Name from EAPOL-Key
|
||||
* message 2/4 */
|
||||
u8 *assoc_resp_ftie;
|
||||
|
||||
void (*ft_pending_cb)(void *ctx, const u8 *dst, const u8 *bssid,
|
||||
u16 auth_transaction, u16 status,
|
||||
const u8 *ies, size_t ies_len);
|
||||
void *ft_pending_cb_ctx;
|
||||
struct wpabuf *ft_pending_req_ies;
|
||||
u8 ft_pending_pull_nonce[FT_R0KH_R1KH_PULL_NONCE_LEN];
|
||||
u8 ft_pending_auth_transaction;
|
||||
u8 ft_pending_current_ap[ETH_ALEN];
|
||||
#endif /* CONFIG_IEEE80211R */
|
||||
|
||||
int pending_1_of_4_timeout;
|
||||
|
|
Loading…
Reference in a new issue