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:
Jouni Malinen 2014-03-23 18:13:03 +02:00
parent 546735476a
commit 692ec3058b
4 changed files with 154 additions and 47 deletions

View file

@ -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);

View file

@ -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];

View file

@ -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,
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,
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,
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;
}

View file

@ -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;