[PATCH 1/6] FT: Allow roaming between APs if IDs match MAC
Benjamin Berg
benjamin at sipsolutions.net
Mon Sep 19 08:47:39 PDT 2016
From: Benjamin Berg <benjamin.berg at open-mesh.com>
This patch adds a new option to set a static key for the communication
between the APs during 802.11r roaming. For this to work hostapd
has to assume that both the nas_identifier and R1KH identifier match
the MAC/BSSID of the hostapd instance.
The advantage is that all APs can share a common configuration and roaming
works throughout the network without each AP having a list of all other
APs and a key for them. It is assumed that all APs can communicate on a
common layer two network.
Signed-off-by: Benjamin Berg <benjamin.berg at open-mesh.com>
---
hostapd/config_file.c | 9 +++
src/ap/ap_config.h | 2 +
src/ap/wpa_auth.h | 2 +
src/ap/wpa_auth_ft.c | 173 +++++++++++++++++++++++++++++++++++++------------
src/ap/wpa_auth_glue.c | 2 +
5 files changed, 146 insertions(+), 42 deletions(-)
diff --git a/hostapd/config_file.c b/hostapd/config_file.c
index 5079f69..a43fa2f 100644
--- a/hostapd/config_file.c
+++ b/hostapd/config_file.c
@@ -2555,6 +2555,15 @@ static int hostapd_config_fill(struct hostapd_config *conf,
line, pos);
return 1;
}
+ } else if (os_strcmp(buf, "ft_remote_key") == 0) {
+
+ bss->ft_remote_trust_khid = 1;
+
+ if (hexstr2bin(pos, bss->ft_remote_key, sizeof(bss->ft_remote_key))) {
+ wpa_printf(MSG_ERROR, "Invalid FT communication key: '%s'", pos);
+ return 1;
+ }
+
} else if (os_strcmp(buf, "pmk_r1_push") == 0) {
bss->pmk_r1_push = atoi(pos);
} else if (os_strcmp(buf, "ft_over_ds") == 0) {
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index 8c8f7e2..5896f90 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -337,6 +337,8 @@ struct hostapd_bss_config {
u32 reassociation_deadline;
struct ft_remote_r0kh *r0kh_list;
struct ft_remote_r1kh *r1kh_list;
+ int ft_remote_trust_khid;
+ u8 ft_remote_key[16];
int pmk_r1_push;
int ft_over_ds;
#endif /* CONFIG_IEEE80211R */
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
index 0de8d97..cd70912 100644
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -168,6 +168,8 @@ struct wpa_auth_config {
u32 reassociation_deadline;
struct ft_remote_r0kh *r0kh_list;
struct ft_remote_r1kh *r1kh_list;
+ int ft_remote_trust_khid;
+ u8 ft_remote_key[16];
int pmk_r1_push;
int ft_over_ds;
#endif /* CONFIG_IEEE80211R */
diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c
index 42242a5..a6e277d 100644
--- a/src/ap/wpa_auth_ft.c
+++ b/src/ap/wpa_auth_ft.c
@@ -300,30 +300,125 @@ static int wpa_ft_fetch_pmk_r1(struct wpa_authenticator *wpa_auth,
return -1;
}
-
-static int wpa_ft_pull_pmk_r1(struct wpa_state_machine *sm,
- const u8 *ies, size_t ies_len,
- const u8 *pmk_r0_name)
+static int wpa_ft_r0kh_find_by_id(struct wpa_authenticator *wpa_auth,
+ const u8 *r0kh_id,
+ int r0kh_id_len,
+ u8 *addr,
+ u8 *key)
{
struct ft_remote_r0kh *r0kh;
- struct ft_r0kh_r1kh_pull_frame frame, f;
- r0kh = sm->wpa_auth->conf.r0kh_list;
+ /* First try to lookup in list. */
+ r0kh = wpa_auth->conf.r0kh_list;
while (r0kh) {
- if (r0kh->id_len == sm->r0kh_id_len &&
- os_memcmp_const(r0kh->id, sm->r0kh_id, sm->r0kh_id_len) ==
+ if (r0kh->id_len == r0kh_id_len &&
+ os_memcmp_const(r0kh->id, r0kh_id, r0kh_id_len) ==
0)
break;
r0kh = r0kh->next;
}
if (r0kh == NULL) {
- wpa_hexdump(MSG_DEBUG, "FT: Did not find R0KH-ID",
+ /* If we trust the KHID to be a MAC, then use this instead.
+ **/
+ if (wpa_auth->conf.ft_remote_trust_khid && r0kh_id_len == (3*6-1)) {
+ if (hwaddr_aton((char*) r0kh_id, addr)) {
+ return -1;
+ }
+ os_memcpy(key, wpa_auth->conf.ft_remote_key,
+ sizeof(wpa_auth->conf.ft_remote_key));
+ } else {
+ return -1;
+ }
+ } else {
+ os_memcpy(key, r0kh->key,
+ sizeof(r0kh->key));
+ os_memcpy(addr, r0kh->addr,
+ ETH_ALEN);
+ }
+
+ return 0;
+}
+
+static int wpa_ft_r0kh_find_by_addr(struct wpa_authenticator *wpa_auth,
+ const u8 *r0kh_addr,
+ u8 *key)
+{
+ struct ft_remote_r0kh *r0kh;
+
+ /* First try to lookup in list. */
+ r0kh = wpa_auth->conf.r0kh_list;
+ while (r0kh) {
+ if (os_memcmp_const(r0kh->addr, r0kh_addr, ETH_ALEN) == 0)
+ break;
+ r0kh = r0kh->next;
+ }
+ if (r0kh == NULL) {
+ /* If we trust the KHID to be a MAC, then use this instead.
+ **/
+ if (wpa_auth->conf.ft_remote_trust_khid) {
+ os_memcpy(key, wpa_auth->conf.ft_remote_key,
+ sizeof(wpa_auth->conf.ft_remote_key));
+ } else {
+ return -1;
+ }
+ } else {
+ os_memcpy(key, r0kh->key,
+ sizeof(r0kh->key));
+ }
+
+ return 0;
+}
+
+static int wpa_ft_r1kh_find_by_addr(struct wpa_authenticator *wpa_auth,
+ const u8 *src_addr,
+ u8 *id,
+ u8 *key)
+{
+ struct ft_remote_r1kh *r1kh;
+
+ /* First try to lookup in list. */
+ r1kh = wpa_auth->conf.r1kh_list;
+ while (r1kh) {
+ if (os_memcmp(r1kh->addr, src_addr, ETH_ALEN) == 0)
+ break;
+ r1kh = r1kh->next;
+ }
+ if (r1kh == NULL) {
+ /* If we trust the KHID to be a MAC, then use this instead.
+ **/
+ if (wpa_auth->conf.ft_remote_trust_khid) {
+ os_memcpy(id, src_addr, ETH_ALEN);
+ os_memcpy(key, wpa_auth->conf.ft_remote_key,
+ sizeof(wpa_auth->conf.ft_remote_key));
+ } else {
+ return -1;
+ }
+ } else {
+ os_memcpy(id, r1kh->id, ETH_ALEN);
+ os_memcpy(key, r1kh->key,
+ sizeof(r1kh->key));
+ }
+
+ return 0;
+}
+
+static int wpa_ft_pull_pmk_r1(struct wpa_state_machine *sm,
+ const u8 *ies, size_t ies_len,
+ const u8 *pmk_r0_name)
+{
+ macaddr r0kh_addr;
+ u8 r0kh_key[16];
+ struct ft_r0kh_r1kh_pull_frame frame, f;
+
+ if (wpa_ft_r0kh_find_by_id(sm->wpa_auth, sm->r0kh_id, sm->r0kh_id_len,
+ r0kh_addr, r0kh_key)) {
+ wpa_hexdump(MSG_DEBUG, "FT: Cannot pull PMK-R1 for R0KH",
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));
+ "address " MACSTR, MAC2STR(r0kh_addr));
os_memset(&frame, 0, sizeof(frame));
frame.frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB;
@@ -345,7 +440,7 @@ static int wpa_ft_pull_pmk_r1(struct wpa_state_machine *sm,
os_memcpy(f.s1kh_id, sm->addr, ETH_ALEN);
os_memset(f.pad, 0, sizeof(f.pad));
- if (aes_wrap(r0kh->key, sizeof(r0kh->key),
+ if (aes_wrap(r0kh_key, sizeof(r0kh_key),
(FT_R0KH_R1KH_PULL_DATA_LEN + 7) / 8,
f.nonce, frame.nonce) < 0)
return -1;
@@ -355,7 +450,7 @@ static int wpa_ft_pull_pmk_r1(struct wpa_state_machine *sm,
if (sm->ft_pending_req_ies == NULL)
return -1;
- wpa_ft_rrb_send(sm->wpa_auth, r0kh->addr, (u8 *) &frame, sizeof(frame));
+ wpa_ft_rrb_send(sm->wpa_auth, r0kh_addr, (u8 *) &frame, sizeof(frame));
return 0;
}
@@ -1313,7 +1408,8 @@ static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth,
struct ft_r0kh_r1kh_pull_frame f;
const u8 *crypt;
u8 *plain;
- struct ft_remote_r1kh *r1kh;
+ u8 r1kh_id[FT_R1KH_ID_LEN];
+ u8 r1kh_key[16];
struct ft_r0kh_r1kh_resp_frame resp, r;
u8 pmk_r0[PMK_LEN];
int pairwise;
@@ -1323,13 +1419,8 @@ static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth,
if (data_len < sizeof(f))
return -1;
- r1kh = wpa_auth->conf.r1kh_list;
- while (r1kh) {
- if (os_memcmp(r1kh->addr, src_addr, ETH_ALEN) == 0)
- break;
- r1kh = r1kh->next;
- }
- if (r1kh == NULL) {
+
+ if (wpa_ft_r1kh_find_by_addr(wpa_auth, src_addr, r1kh_id, r1kh_key) != 0) {
wpa_printf(MSG_DEBUG, "FT: No matching R1KH address found for "
"PMK-R1 pull source address " MACSTR,
MAC2STR(src_addr));
@@ -1341,7 +1432,7 @@ static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth,
plain = ((u8 *) &f) + offsetof(struct ft_r0kh_r1kh_pull_frame, nonce);
/* aes_unwrap() does not support inplace decryption, so use a temporary
* buffer for the data. */
- if (aes_unwrap(r1kh->key, sizeof(r1kh->key),
+ if (aes_unwrap(r1kh_key, sizeof(r1kh_key),
(FT_R0KH_R1KH_PULL_DATA_LEN + 7) / 8,
crypt, plain) < 0) {
wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 pull "
@@ -1349,6 +1440,16 @@ static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth,
return -1;
}
+ /* Ensure that R1KH-ID matches the expected value. */
+ if (os_memcmp(r1kh_id, f.r1kh_id, FT_R1KH_ID_LEN) != 0) {
+ wpa_printf(MSG_ERROR, "FT: PMK-R1 pull included R1KH_ID "
+ "(" MACSTR ") that did not match the packet "
+ "senders ID (" MACSTR ", MAC " MACSTR ")",
+ MAC2STR(f.r1kh_id), MAC2STR(r1kh_id),
+ MAC2STR(src_addr));
+ return -1;
+ }
+
wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - nonce",
f.nonce, sizeof(f.nonce));
wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - PMKR0Name",
@@ -1382,7 +1483,7 @@ static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth,
r.pairwise = host_to_le16(pairwise);
os_memset(r.pad, 0, sizeof(r.pad));
- if (aes_wrap(r1kh->key, sizeof(r1kh->key),
+ if (aes_wrap(r1kh_key, sizeof(r1kh_key),
(FT_R0KH_R1KH_RESP_DATA_LEN + 7) / 8,
r.nonce, resp.nonce) < 0) {
os_memset(pmk_r0, 0, PMK_LEN);
@@ -1449,7 +1550,7 @@ static int wpa_ft_rrb_rx_resp(struct wpa_authenticator *wpa_auth,
struct ft_r0kh_r1kh_resp_frame f;
const u8 *crypt;
u8 *plain;
- struct ft_remote_r0kh *r0kh;
+ u8 r0kh_key[16];
int pairwise, res;
wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 pull response");
@@ -1457,14 +1558,8 @@ static int wpa_ft_rrb_rx_resp(struct wpa_authenticator *wpa_auth,
if (data_len < sizeof(f))
return -1;
- r0kh = wpa_auth->conf.r0kh_list;
- while (r0kh) {
- if (os_memcmp(r0kh->addr, src_addr, ETH_ALEN) == 0)
- break;
- r0kh = r0kh->next;
- }
- if (r0kh == NULL) {
- wpa_printf(MSG_DEBUG, "FT: No matching R0KH address found for "
+ if (wpa_ft_r0kh_find_by_addr(wpa_auth, src_addr, r0kh_key)) {
+ wpa_printf(MSG_DEBUG, "FT: No R0KH address found for "
"PMK-R0 pull response source address " MACSTR,
MAC2STR(src_addr));
return -1;
@@ -1475,7 +1570,7 @@ static int wpa_ft_rrb_rx_resp(struct wpa_authenticator *wpa_auth,
plain = ((u8 *) &f) + offsetof(struct ft_r0kh_r1kh_resp_frame, nonce);
/* aes_unwrap() does not support inplace decryption, so use a temporary
* buffer for the data. */
- if (aes_unwrap(r0kh->key, sizeof(r0kh->key),
+ if (aes_unwrap(r0kh_key, sizeof(r0kh_key),
(FT_R0KH_R1KH_RESP_DATA_LEN + 7) / 8,
crypt, plain) < 0) {
wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 pull "
@@ -1518,7 +1613,7 @@ static int wpa_ft_rrb_rx_push(struct wpa_authenticator *wpa_auth,
struct ft_r0kh_r1kh_push_frame f;
const u8 *crypt;
u8 *plain;
- struct ft_remote_r0kh *r0kh;
+ u8 r0kh_key[16];
struct os_time now;
os_time_t tsend;
int pairwise;
@@ -1528,14 +1623,8 @@ static int wpa_ft_rrb_rx_push(struct wpa_authenticator *wpa_auth,
if (data_len < sizeof(f))
return -1;
- r0kh = wpa_auth->conf.r0kh_list;
- while (r0kh) {
- if (os_memcmp(r0kh->addr, src_addr, ETH_ALEN) == 0)
- break;
- r0kh = r0kh->next;
- }
- if (r0kh == NULL) {
- wpa_printf(MSG_DEBUG, "FT: No matching R0KH address found for "
+ if (wpa_ft_r0kh_find_by_addr(wpa_auth, src_addr, r0kh_key)) {
+ wpa_printf(MSG_DEBUG, "FT: No R0KH address found for "
"PMK-R0 push source address " MACSTR,
MAC2STR(src_addr));
return -1;
@@ -1547,7 +1636,7 @@ static int wpa_ft_rrb_rx_push(struct wpa_authenticator *wpa_auth,
timestamp);
/* aes_unwrap() does not support inplace decryption, so use a temporary
* buffer for the data. */
- if (aes_unwrap(r0kh->key, sizeof(r0kh->key),
+ if (aes_unwrap(r0kh_key, sizeof(r0kh_key),
(FT_R0KH_R1KH_PUSH_DATA_LEN + 7) / 8,
crypt, plain) < 0) {
wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 push from "
diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
index 2142414..90e39a4 100644
--- a/src/ap/wpa_auth_glue.c
+++ b/src/ap/wpa_auth_glue.c
@@ -71,6 +71,8 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf,
wconf->reassociation_deadline = conf->reassociation_deadline;
wconf->r0kh_list = conf->r0kh_list;
wconf->r1kh_list = conf->r1kh_list;
+ wconf->ft_remote_trust_khid = conf->ft_remote_trust_khid;
+ os_memcpy(wconf->ft_remote_key, conf->ft_remote_key, sizeof(wconf->ft_remote_key));
wconf->pmk_r1_push = conf->pmk_r1_push;
wconf->ft_over_ds = conf->ft_over_ds;
#endif /* CONFIG_IEEE80211R */
--
2.9.3
More information about the Hostap
mailing list