aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Braun <michael-dev@fami-braun.de>2017-04-02 12:52:51 (GMT)
committerJouni Malinen <j@w1.fi>2017-05-03 19:16:14 (GMT)
commiteefe8630153f79fdabf004ba32d2ad7527381886 (patch)
treef6614098b4a01e6dd1e364c5bdc3b437c62a8117
parentc95dd8e48bc5a5ec0a2bb4284e845ba40c5ff95b (diff)
downloadhostap-eefe8630153f79fdabf004ba32d2ad7527381886.zip
hostap-eefe8630153f79fdabf004ba32d2ad7527381886.tar.gz
hostap-eefe8630153f79fdabf004ba32d2ad7527381886.tar.bz2
FT RRB: Add msg replay and msg delay protection
This adds a counter and adds sequence numbering to FT RRB packets. The sequence number is checked against r0kh/r1kh sequence number cache. Special attention is needed in case the remote AP reboots and thus loses its state. I prefer it to recover automatically even without synchronized clocks. Therefore an identifier called dom is generated randomly along the initial sequence number. If the dom transmitted does not match or the sequence number is not in the range currently expected, the sender is asked for a fresh confirmation of its currently used sequence numbers. The packet that triggered this is cached and processed again later. Additionally, in order to ensure freshness, the remote AP includes an timestamp with its messages. It is then verified that the received messages are indeed fresh by comparing it to the older timestamps received and the time elapsed since then. Therefore FT_RRB_TIMESTAMP is no longer needed. This assigns new OUI 00:13:74 vendor-specific subtype 0x0001 subtypes: 4 (SEQ_REQ) and 5 (SEQ_RESP). This breaks backward compatibility, i.e., hostapd needs to be updated on all APs at the same time to allow FT to remain functional. Signed-off-by: Michael Braun <michael-dev@fami-braun.de>
-rw-r--r--src/ap/wpa_auth.c1
-rw-r--r--src/ap/wpa_auth.h27
-rw-r--r--src/ap/wpa_auth_ft.c787
-rw-r--r--src/ap/wpa_auth_glue.c20
-rw-r--r--src/ap/wpa_auth_i.h34
5 files changed, 796 insertions, 73 deletions
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index 172f20f..9fceca6 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -520,6 +520,7 @@ void wpa_deinit(struct wpa_authenticator *wpa_auth)
#ifdef CONFIG_IEEE80211R_AP
wpa_ft_pmk_cache_deinit(wpa_auth->ft_pmk_cache);
wpa_auth->ft_pmk_cache = NULL;
+ wpa_ft_deinit(wpa_auth);
#endif /* CONFIG_IEEE80211R_AP */
#ifdef CONFIG_P2P
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
index b9cdcac..bab5619 100644
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -43,6 +43,8 @@ struct ft_rrb_frame {
#define FT_PACKET_R0KH_R1KH_PULL 0x01
#define FT_PACKET_R0KH_R1KH_RESP 0x02
#define FT_PACKET_R0KH_R1KH_PUSH 0x03
+#define FT_PACKET_R0KH_R1KH_SEQ_REQ 0x04
+#define FT_PACKET_R0KH_R1KH_SEQ_RESP 0x05
/* packet layout
* IEEE 802 extended OUI ethertype frame header
@@ -61,6 +63,7 @@ struct ft_rrb_frame {
#define FT_RRB_LAST_EMPTY 0 /* placeholder or padding */
+#define FT_RRB_SEQ 1 /* struct ft_rrb_seq */
#define FT_RRB_NONCE 2 /* size FT_RRB_NONCE_LEN */
#define FT_RRB_TIMESTAMP 3 /* le32 unix seconds */
@@ -81,26 +84,40 @@ struct ft_rrb_tlv {
/* followed by data of length len */
} STRUCT_PACKED;
+struct ft_rrb_seq {
+ le32 dom;
+ le32 seq;
+ le32 ts;
+} STRUCT_PACKED;
+
/* session TLVs:
* required: PMK_R1, PMK_R1_NAME, PAIRWISE
*
* pull frame TLVs:
* auth:
- * required: NONCE, R0KH_ID, R1KH_ID
+ * required: SEQ, NONCE, R0KH_ID, R1KH_ID
* encrypted:
* required: PMK_R0_NAME, S1KH_ID
*
* response frame TLVs:
* auth:
- * required: NONCE, R0KH_ID, R1KH_ID
+ * required: SEQ, NONCE, R0KH_ID, R1KH_ID
* encrypted:
* required: S1KH_ID, session TLVs
*
* push frame TLVs:
* auth:
- * required: TIMESTAMP, R0KH_ID, R1KH_ID
+ * required: SEQ, R0KH_ID, R1KH_ID
* encrypted:
* required: S1KH_ID, PMK_R0_NAME, session TLVs
+ *
+ * sequence number request frame TLVs:
+ * auth:
+ * required: R0KH_ID, R1KH_ID, NONCE
+ *
+ * sequence number response frame TLVs:
+ * auth:
+ * required: SEQ, NONCE, R0KH_ID, R1KH_ID
*/
#ifdef _MSC_VER
@@ -114,6 +131,7 @@ struct wpa_authenticator;
struct wpa_state_machine;
struct rsn_pmksa_cache_entry;
struct eapol_state_machine;
+struct ft_remote_seq;
struct ft_remote_r0kh {
@@ -122,6 +140,7 @@ struct ft_remote_r0kh {
u8 id[FT_R0KH_ID_MAX_LEN];
size_t id_len;
u8 key[32];
+ struct ft_remote_seq *seq;
};
@@ -130,6 +149,7 @@ struct ft_remote_r1kh {
u8 addr[ETH_ALEN];
u8 id[FT_R1KH_ID_LEN];
u8 key[32];
+ struct ft_remote_seq *seq;
};
@@ -349,6 +369,7 @@ void wpa_ft_rrb_oui_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr,
const u8 *dst_addr, u8 oui_suffix, const u8 *data,
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);
#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 885b855..9db9b26 100644
--- a/src/ap/wpa_auth_ft.c
+++ b/src/ap/wpa_auth_ft.c
@@ -26,10 +26,15 @@
#ifdef CONFIG_IEEE80211R_AP
+const unsigned int ftRRBseqTimeout = 10;
+const unsigned int ftRRBmaxQueueLen = 100;
+
+
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 wpa_ft_rrb_seq_timeout(void *eloop_ctx, void *timeout_ctx);
struct tlv_list {
u16 type;
@@ -531,6 +536,301 @@ int wpa_write_ftie(struct wpa_auth_config *conf, const u8 *r0kh_id,
}
+/* A packet to be handled after seq response */
+struct ft_remote_item {
+ struct dl_list list;
+
+ u8 nonce[FT_RRB_NONCE_LEN];
+ struct os_reltime nonce_ts;
+
+ u8 src_addr[ETH_ALEN];
+ u8 *enc;
+ size_t enc_len;
+ u8 *auth;
+ size_t auth_len;
+ int (*cb)(struct wpa_authenticator *wpa_auth,
+ const u8 *src_addr,
+ const u8 *enc, size_t enc_len,
+ const u8 *auth, size_t auth_len,
+ int no_defer);
+};
+
+
+static void wpa_ft_rrb_seq_free(struct ft_remote_item *item)
+{
+ eloop_cancel_timeout(wpa_ft_rrb_seq_timeout, ELOOP_ALL_CTX, item);
+ dl_list_del(&item->list);
+ bin_clear_free(item->enc, item->enc_len);
+ os_free(item->auth);
+ os_free(item);
+}
+
+
+static void wpa_ft_rrb_seq_flush(struct wpa_authenticator *wpa_auth,
+ struct ft_remote_seq *rkh_seq, int cb)
+{
+ struct ft_remote_item *item, *n;
+
+ dl_list_for_each_safe(item, n, &rkh_seq->rx.queue,
+ struct ft_remote_item, list) {
+ if (cb && item->cb)
+ item->cb(wpa_auth, item->src_addr, item->enc,
+ item->enc_len, item->auth, item->auth_len, 1);
+ wpa_ft_rrb_seq_free(item);
+ }
+}
+
+
+static void wpa_ft_rrb_seq_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct ft_remote_item *item = timeout_ctx;
+
+ wpa_ft_rrb_seq_free(item);
+}
+
+
+static int
+wpa_ft_rrb_seq_req(struct wpa_authenticator *wpa_auth,
+ struct ft_remote_seq *rkh_seq, const u8 *src_addr,
+ const u8 *f_r0kh_id, size_t f_r0kh_id_len,
+ const u8 *f_r1kh_id, const u8 *key, size_t key_len,
+ const u8 *enc, size_t enc_len,
+ const u8 *auth, size_t auth_len,
+ int (*cb)(struct wpa_authenticator *wpa_auth,
+ const u8 *src_addr,
+ const u8 *enc, size_t enc_len,
+ const u8 *auth, size_t auth_len,
+ int no_defer))
+{
+ struct ft_remote_item *item = NULL;
+ u8 *packet = NULL;
+ size_t packet_len;
+ struct tlv_list seq_req_auth[] = {
+ { .type = FT_RRB_NONCE, .len = FT_RRB_NONCE_LEN,
+ .data = NULL /* to be filled: item->nonce */ },
+ { .type = FT_RRB_R0KH_ID, .len = f_r0kh_id_len,
+ .data = f_r0kh_id },
+ { .type = FT_RRB_R1KH_ID, .len = FT_R1KH_ID_LEN,
+ .data = f_r1kh_id },
+ { .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL },
+ };
+
+ if (dl_list_len(&rkh_seq->rx.queue) >= ftRRBmaxQueueLen) {
+ wpa_printf(MSG_DEBUG, "FT: Sequence number queue too long");
+ goto err;
+ }
+
+ item = os_zalloc(sizeof(*item));
+ if (!item)
+ goto err;
+
+ os_memcpy(item->src_addr, src_addr, ETH_ALEN);
+ item->cb = cb;
+
+ if (random_get_bytes(item->nonce, FT_RRB_NONCE_LEN) < 0) {
+ wpa_printf(MSG_DEBUG, "FT: Seq num nonce: out of random bytes");
+ goto err;
+ }
+
+ if (os_get_reltime(&item->nonce_ts) < 0)
+ goto err;
+
+ if (enc && enc_len > 0) {
+ item->enc = os_memdup(enc, enc_len);
+ item->enc_len = enc_len;
+ if (!item->enc)
+ goto err;
+ }
+
+ if (auth && auth_len > 0) {
+ item->auth = os_memdup(auth, auth_len);
+ item->auth_len = auth_len;
+ if (!item->auth)
+ goto err;
+ }
+
+ eloop_register_timeout(ftRRBseqTimeout, 0, wpa_ft_rrb_seq_timeout,
+ wpa_auth, item);
+
+ seq_req_auth[0].data = item->nonce;
+
+ if (wpa_ft_rrb_build(key, key_len, NULL, NULL, seq_req_auth,
+ wpa_auth->addr, FT_PACKET_R0KH_R1KH_SEQ_REQ,
+ &packet, &packet_len) < 0) {
+ item = NULL; /* some other seq resp might still accept this */
+ goto err;
+ }
+
+ dl_list_add(&rkh_seq->rx.queue, &item->list);
+
+ wpa_ft_rrb_oui_send(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_SEQ_REQ,
+ packet, packet_len);
+
+ os_free(packet);
+
+ return 0;
+err:
+ wpa_printf(MSG_DEBUG, "FT: Failed to send sequence number request");
+ if (item) {
+ os_free(item->auth);
+ bin_clear_free(item->enc, item->enc_len);
+ os_free(item);
+ }
+
+ return -1;
+}
+
+
+#define FT_RRB_SEQ_OK 0
+#define FT_RRB_SEQ_DROP 1
+#define FT_RRB_SEQ_DEFER 2
+
+static int
+wpa_ft_rrb_seq_chk(struct ft_remote_seq *rkh_seq, const u8 *src_addr,
+ const u8 *enc, size_t enc_len,
+ const u8 *auth, size_t auth_len,
+ const char *msgtype, int no_defer)
+{
+ const u8 *f_seq;
+ size_t f_seq_len;
+ const struct ft_rrb_seq *msg_both;
+ u32 msg_seq, msg_off, rkh_off;
+ struct os_reltime now;
+ unsigned int i;
+
+ RRB_GET_AUTH(FT_RRB_SEQ, seq, msgtype, sizeof(*msg_both));
+ wpa_hexdump(MSG_DEBUG, "FT: sequence number", f_seq, f_seq_len);
+ msg_both = (const struct ft_rrb_seq *) f_seq;
+
+ if (rkh_seq->rx.num_last == 0) {
+ /* first packet from remote */
+ goto defer;
+ }
+
+ if (le_to_host32(msg_both->dom) != rkh_seq->rx.dom) {
+ /* remote might have rebooted */
+ goto defer;
+ }
+
+ if (os_get_reltime(&now) == 0) {
+ u32 msg_ts_now_remote, msg_ts_off;
+ struct os_reltime now_remote;
+
+ os_reltime_sub(&now, &rkh_seq->rx.time_offset, &now_remote);
+ msg_ts_now_remote = now_remote.sec;
+ msg_ts_off = le_to_host32(msg_both->ts) -
+ (msg_ts_now_remote - ftRRBseqTimeout);
+ if (msg_ts_off > 2 * ftRRBseqTimeout)
+ goto defer;
+ }
+
+ msg_seq = le_to_host32(msg_both->seq);
+ rkh_off = rkh_seq->rx.last[rkh_seq->rx.offsetidx];
+ msg_off = msg_seq - rkh_off;
+ if (msg_off > 0xC0000000)
+ goto out; /* too old message, drop it */
+
+ if (msg_off <= 0x40000000) {
+ for (i = 0; i < rkh_seq->rx.num_last; i++) {
+ if (rkh_seq->rx.last[i] == msg_seq)
+ goto out; /* duplicate message, drop it */
+ }
+
+ return FT_RRB_SEQ_OK;
+ }
+
+defer:
+ if (no_defer)
+ goto out;
+
+ wpa_printf(MSG_DEBUG, "FT: Possibly invalid sequence number in %s from "
+ MACSTR, msgtype, MAC2STR(src_addr));
+
+ return FT_RRB_SEQ_DEFER;
+out:
+ wpa_printf(MSG_DEBUG, "FT: Invalid sequence number in %s from " MACSTR,
+ msgtype, MAC2STR(src_addr));
+
+ return FT_RRB_SEQ_DROP;
+}
+
+
+static void
+wpa_ft_rrb_seq_accept(struct wpa_authenticator *wpa_auth,
+ struct ft_remote_seq *rkh_seq, const u8 *src_addr,
+ const u8 *auth, size_t auth_len,
+ const char *msgtype)
+{
+ const u8 *f_seq;
+ size_t f_seq_len;
+ const struct ft_rrb_seq *msg_both;
+ u32 msg_seq, msg_off, min_off, rkh_off;
+ int minidx = 0;
+ unsigned int i;
+
+ RRB_GET_AUTH(FT_RRB_SEQ, seq, msgtype, sizeof(*msg_both));
+ msg_both = (const struct ft_rrb_seq *) f_seq;
+
+ msg_seq = le_to_host32(msg_both->seq);
+
+ if (rkh_seq->rx.num_last < FT_REMOTE_SEQ_BACKLOG) {
+ rkh_seq->rx.last[rkh_seq->rx.num_last] = msg_seq;
+ rkh_seq->rx.num_last++;
+ return;
+ }
+
+ rkh_off = rkh_seq->rx.last[rkh_seq->rx.offsetidx];
+ for (i = 0; i < rkh_seq->rx.num_last; i++) {
+ msg_off = rkh_seq->rx.last[i] - rkh_off;
+ min_off = rkh_seq->rx.last[minidx] - rkh_off;
+ if (msg_off < min_off && i != rkh_seq->rx.offsetidx)
+ minidx = i;
+ }
+ rkh_seq->rx.last[rkh_seq->rx.offsetidx] = msg_seq;
+ rkh_seq->rx.offsetidx = minidx;
+
+ return;
+out:
+ /* RRB_GET_AUTH should never fail here as
+ * wpa_ft_rrb_seq_chk() verified FT_RRB_SEQ presence. */
+ wpa_printf(MSG_ERROR, "FT: %s() failed", __func__);
+}
+
+
+static int wpa_ft_new_seq(struct ft_remote_seq *rkh_seq,
+ struct ft_rrb_seq *f_seq)
+{
+ struct os_reltime now;
+
+ if (os_get_reltime(&now) < 0)
+ return -1;
+
+ if (!rkh_seq->tx.dom) {
+ if (random_get_bytes((u8 *) &rkh_seq->tx.seq,
+ sizeof(rkh_seq->tx.seq))) {
+ wpa_printf(MSG_ERROR,
+ "FT: Failed to get random data for sequence number initialization");
+ rkh_seq->tx.seq = now.usec;
+ }
+ if (random_get_bytes((u8 *) &rkh_seq->tx.dom,
+ sizeof(rkh_seq->tx.dom))) {
+ wpa_printf(MSG_ERROR,
+ "FT: Failed to get random data for sequence number initialization");
+ rkh_seq->tx.dom = now.usec;
+ }
+ rkh_seq->tx.dom |= 1;
+ }
+
+ f_seq->dom = host_to_le32(rkh_seq->tx.dom);
+ f_seq->seq = host_to_le32(rkh_seq->tx.seq);
+ f_seq->ts = host_to_le32(now.sec);
+
+ rkh_seq->tx.seq++;
+
+ return 0;
+}
+
+
struct wpa_ft_pmk_r0_sa {
struct wpa_ft_pmk_r0_sa *next;
u8 pmk_r0[PMK_LEN];
@@ -689,49 +989,85 @@ static int wpa_ft_fetch_pmk_r1(struct wpa_authenticator *wpa_auth,
}
+static int wpa_ft_rrb_init_r0kh_seq(struct ft_remote_r0kh *r0kh)
+{
+ if (r0kh->seq)
+ return 0;
+
+ r0kh->seq = os_zalloc(sizeof(*r0kh->seq));
+ if (!r0kh->seq) {
+ wpa_printf(MSG_DEBUG, "FT: Failed to allocate r0kh->seq");
+ return -1;
+ }
+
+ dl_list_init(&r0kh->seq->rx.queue);
+
+ return 0;
+}
+
+
static void wpa_ft_rrb_lookup_r0kh(struct wpa_authenticator *wpa_auth,
- const u8 *src_addr, 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;
+ *r0kh_out = NULL;
+
for (r0kh = wpa_auth->conf.r0kh_list; r0kh; r0kh = r0kh->next) {
- if (src_addr && os_memcmp(r0kh->addr, src_addr, ETH_ALEN) != 0)
- continue;
- if (f_r0kh_id &&
- (r0kh->id_len != f_r0kh_id_len ||
- os_memcmp_const(f_r0kh_id, r0kh->id, f_r0kh_id_len) != 0))
- continue;
- break;
+ if (f_r0kh_id && r0kh->id_len == f_r0kh_id_len &&
+ os_memcmp_const(f_r0kh_id, r0kh->id, f_r0kh_id_len) == 0) {
+ *r0kh_out = r0kh;
+ break;
+ }
}
- if (!r0kh)
+ if (!*r0kh_out)
wpa_printf(MSG_DEBUG, "FT: No matching R0KH found");
- *r0kh_out = r0kh;
+ if (*r0kh_out && wpa_ft_rrb_init_r0kh_seq(*r0kh_out) < 0)
+ *r0kh_out = NULL;
+}
+
+
+static int wpa_ft_rrb_init_r1kh_seq(struct ft_remote_r1kh *r1kh)
+{
+ if (r1kh->seq)
+ return 0;
+
+ r1kh->seq = os_zalloc(sizeof(*r1kh->seq));
+ if (!r1kh->seq) {
+ wpa_printf(MSG_DEBUG, "FT: Failed to allocate r1kh->seq");
+ return -1;
+ }
+
+ dl_list_init(&r1kh->seq->rx.queue);
+
+ return 0;
}
static void wpa_ft_rrb_lookup_r1kh(struct wpa_authenticator *wpa_auth,
- const u8 *src_addr, const u8 *f_r1kh_id,
+ const u8 *f_r1kh_id,
struct ft_remote_r1kh **r1kh_out)
{
struct ft_remote_r1kh *r1kh;
+ *r1kh_out = NULL;
+
for (r1kh = wpa_auth->conf.r1kh_list; r1kh; r1kh = r1kh->next) {
- if (src_addr && os_memcmp(r1kh->addr, src_addr, ETH_ALEN) != 0)
- continue;
if (f_r1kh_id &&
- os_memcmp_const(r1kh->id, f_r1kh_id, FT_R1KH_ID_LEN) != 0)
- continue;
- break;
+ os_memcmp_const(r1kh->id, f_r1kh_id, FT_R1KH_ID_LEN) == 0) {
+ *r1kh_out = r1kh;
+ break;
+ }
}
- if (!r1kh)
+ if (!*r1kh_out)
wpa_printf(MSG_DEBUG, "FT: No matching R1KH found");
- *r1kh_out = r1kh;
+ if (*r1kh_out && wpa_ft_rrb_init_r1kh_seq(*r1kh_out) < 0)
+ *r1kh_out = NULL;
}
@@ -758,14 +1094,46 @@ static int wpa_ft_rrb_check_r1kh(struct wpa_authenticator *wpa_auth,
}
+static void wpa_ft_deinit_seq(struct wpa_authenticator *wpa_auth)
+{
+ struct ft_remote_r0kh *r0kh;
+ struct ft_remote_r1kh *r1kh;
+
+ 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 (!r0kh->seq)
+ continue;
+ wpa_ft_rrb_seq_flush(wpa_auth, r0kh->seq, 0);
+ os_free(r0kh->seq);
+ r0kh->seq = NULL;
+ }
+
+ for (r1kh = wpa_auth->conf.r1kh_list; r1kh; r1kh = r1kh->next) {
+ if (!r1kh->seq)
+ continue;
+ wpa_ft_rrb_seq_flush(wpa_auth, r1kh->seq, 0);
+ os_free(r1kh->seq);
+ r1kh->seq = NULL;
+ }
+}
+
+
+void wpa_ft_deinit(struct wpa_authenticator *wpa_auth)
+{
+ wpa_ft_deinit_seq(wpa_auth);
+}
+
+
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;
u8 *packet = NULL;
- const u8 *key;
+ 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;
struct tlv_list req_enc[] = {
{ .type = FT_RRB_PMK_R0_NAME, .len = WPA_PMK_NAME_LEN,
.data = pmk_r0_name },
@@ -776,32 +1144,48 @@ static int wpa_ft_pull_pmk_r1(struct wpa_state_machine *sm,
struct tlv_list req_auth[] = {
{ .type = FT_RRB_NONCE, .len = FT_RRB_NONCE_LEN,
.data = sm->ft_pending_pull_nonce },
+ { .type = FT_RRB_SEQ, .len = sizeof(f_seq),
+ .data = (u8 *) &f_seq },
{ .type = FT_RRB_R0KH_ID, .len = sm->r0kh_id_len,
.data = sm->r0kh_id },
{ .type = FT_RRB_R1KH_ID, .len = FT_R1KH_ID_LEN,
- .data = sm->wpa_auth->conf.r1_key_holder },
+ .data = f_r1kh_id },
{ .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL },
};
- wpa_ft_rrb_lookup_r0kh(sm->wpa_auth, NULL, sm->r0kh_id, sm->r0kh_id_len,
+ wpa_ft_rrb_lookup_r0kh(sm->wpa_auth, sm->r0kh_id, sm->r0kh_id_len,
&r0kh);
if (r0kh == NULL) {
wpa_hexdump(MSG_DEBUG, "FT: Did not find R0KH-ID",
sm->r0kh_id, sm->r0kh_id_len);
return -1;
}
+
key = r0kh->key;
key_len = sizeof(r0kh->key);
wpa_printf(MSG_DEBUG, "FT: Send PMK-R1 pull request to remote R0KH "
"address " MACSTR, MAC2STR(r0kh->addr));
+ if (r0kh->seq->rx.num_last == 0) {
+ /* A sequence request will be sent out anyway when pull
+ * response is received. Send it out now to avoid one RTT. */
+ wpa_ft_rrb_seq_req(sm->wpa_auth, r0kh->seq, r0kh->addr,
+ 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) {
wpa_printf(MSG_DEBUG, "FT: Failed to get random data for "
"nonce");
return -1;
}
+ if (wpa_ft_new_seq(r0kh->seq, &f_seq) < 0) {
+ wpa_printf(MSG_DEBUG, "FT: Failed to get seq num");
+ return -1;
+ }
+
if (wpa_ft_rrb_build(key, key_len, req_enc, NULL, req_auth,
sm->wpa_auth->addr, FT_PACKET_R0KH_R1KH_PULL,
&packet, &packet_len) < 0)
@@ -1911,7 +2295,8 @@ static int wpa_ft_rrb_build_r0(const u8 *key, const size_t key_len,
static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth,
const u8 *src_addr,
const u8 *enc, size_t enc_len,
- const u8 *auth, size_t auth_len)
+ const u8 *auth, size_t auth_len,
+ int no_defer)
{
const char *msgtype = "pull request";
u8 *plain = NULL, *packet = NULL;
@@ -1919,13 +2304,15 @@ static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth,
struct ft_remote_r1kh *r1kh;
const u8 *key;
size_t key_len;
+ int seq_ret;
const u8 *f_nonce, *f_r0kh_id, *f_r1kh_id, *f_s1kh_id, *f_pmk_r0_name;
size_t f_nonce_len, f_r0kh_id_len, f_r1kh_id_len, f_s1kh_id_len;
size_t f_pmk_r0_name_len;
const struct wpa_ft_pmk_r0_sa *r0;
int ret;
struct tlv_list resp[2];
- struct tlv_list resp_auth[4];
+ struct tlv_list resp_auth[5];
+ struct ft_rrb_seq f_seq;
wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 pull");
@@ -1940,8 +2327,8 @@ 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, src_addr, f_r1kh_id, &r1kh);
- if (!r1kh)
+ wpa_ft_rrb_lookup_r1kh(wpa_auth, f_r1kh_id, &r1kh);
+ if (!r1kh || os_memcmp(r1kh->addr, src_addr, ETH_ALEN) != 0)
goto out;
key = r1kh->key;
key_len = sizeof(r1kh->key);
@@ -1949,11 +2336,27 @@ static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth,
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, no_defer);
+ if (seq_ret == FT_RRB_SEQ_DROP)
+ goto out;
+
if (wpa_ft_rrb_decrypt(key, key_len, enc, enc_len, auth, auth_len,
src_addr, FT_PACKET_R0KH_R1KH_PULL,
&plain, &plain_len) < 0)
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,
+ enc, enc_len, auth, auth_len,
+ &wpa_ft_rrb_rx_pull);
+ goto out;
+ }
+
+ wpa_ft_rrb_seq_accept(wpa_auth, r1kh->seq, src_addr, auth, auth_len,
+ msgtype);
+
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,
f_pmk_r0_name_len);
@@ -1961,6 +2364,11 @@ static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth,
RRB_GET(FT_RRB_S1KH_ID, s1kh_id, msgtype, ETH_ALEN);
wpa_printf(MSG_DEBUG, "FT: S1KH-ID=" MACSTR, MAC2STR(f_s1kh_id));
+ if (wpa_ft_new_seq(r1kh->seq, &f_seq) < 0) {
+ wpa_printf(MSG_DEBUG, "FT: Failed to get seq num");
+ goto out;
+ }
+
resp[0].type = FT_RRB_S1KH_ID;
resp[0].len = f_s1kh_id_len;
resp[0].data = f_s1kh_id;
@@ -1971,15 +2379,18 @@ static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth,
resp_auth[0].type = FT_RRB_NONCE;
resp_auth[0].len = f_nonce_len;
resp_auth[0].data = f_nonce;
- resp_auth[1].type = FT_RRB_R0KH_ID;
- resp_auth[1].len = f_r0kh_id_len;
- resp_auth[1].data = f_r0kh_id;
- resp_auth[2].type = FT_RRB_R1KH_ID;
- resp_auth[2].len = f_r1kh_id_len;
- resp_auth[2].data = f_r1kh_id;
- resp_auth[3].type = FT_RRB_LAST_EMPTY;
- resp_auth[3].len = 0;
- resp_auth[3].data = NULL;
+ resp_auth[1].type = FT_RRB_SEQ;
+ resp_auth[1].len = sizeof(f_seq);
+ resp_auth[1].data = (u8 *) &f_seq;
+ resp_auth[2].type = FT_RRB_R0KH_ID;
+ resp_auth[2].len = f_r0kh_id_len;
+ resp_auth[2].data = f_r0kh_id;
+ resp_auth[3].type = FT_RRB_R1KH_ID;
+ resp_auth[3].len = f_r1kh_id_len;
+ resp_auth[3].data = f_r1kh_id;
+ resp_auth[4].type = FT_RRB_LAST_EMPTY;
+ resp_auth[4].len = 0;
+ resp_auth[4].data = NULL;
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");
@@ -2011,13 +2422,19 @@ static int wpa_ft_rrb_rx_r1(struct wpa_authenticator *wpa_auth,
const u8 *src_addr, u8 type,
const u8 *enc, size_t enc_len,
const u8 *auth, size_t auth_len,
- const char *msgtype, u8 *s1kh_id_out)
+ const char *msgtype, u8 *s1kh_id_out,
+ int (*cb)(struct wpa_authenticator *wpa_auth,
+ const u8 *src_addr,
+ const u8 *enc, size_t enc_len,
+ const u8 *auth, size_t auth_len,
+ int no_defer))
{
u8 *plain = NULL;
size_t plain_len = 0;
struct ft_remote_r0kh *r0kh;
const u8 *key;
size_t key_len;
+ int seq_ret;
const u8 *f_r1kh_id, *f_s1kh_id, *f_r0kh_id;
const u8 *f_pmk_r1_name, *f_pairwise, *f_pmk_r1;
size_t f_r1kh_id_len, f_s1kh_id_len, f_r0kh_id_len;
@@ -2036,17 +2453,31 @@ static int wpa_ft_rrb_rx_r1(struct wpa_authenticator *wpa_auth,
goto out;
}
- wpa_ft_rrb_lookup_r0kh(wpa_auth, src_addr, f_r0kh_id, f_r0kh_id_len,
- &r0kh);
- if (!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)
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);
+ if (seq_ret == FT_RRB_SEQ_DROP)
+ goto out;
+
if (wpa_ft_rrb_decrypt(key, key_len, enc, enc_len, auth, auth_len,
src_addr, type, &plain, &plain_len) < 0)
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,
+ enc, enc_len, auth, auth_len, cb);
+ goto out;
+ }
+
+ wpa_ft_rrb_seq_accept(wpa_auth, r0kh->seq, src_addr, auth, auth_len,
+ msgtype);
+
RRB_GET(FT_RRB_S1KH_ID, s1kh_id, msgtype, ETH_ALEN);
wpa_printf(MSG_DEBUG, "FT: S1KH-ID=" MACSTR, MAC2STR(f_s1kh_id));
@@ -2133,7 +2564,8 @@ static int ft_get_sta_cb(struct wpa_state_machine *sm, void *ctx)
static int wpa_ft_rrb_rx_resp(struct wpa_authenticator *wpa_auth,
const u8 *src_addr,
const u8 *enc, size_t enc_len,
- const u8 *auth, size_t auth_len)
+ const u8 *auth, size_t auth_len,
+ int no_defer)
{
const char *msgtype = "pull response";
int ret = -1;
@@ -2156,7 +2588,8 @@ 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,
- 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);
if (ret < 0)
return -1;
@@ -2176,31 +2609,233 @@ out:
static int wpa_ft_rrb_rx_push(struct wpa_authenticator *wpa_auth,
const u8 *src_addr,
const u8 *enc, size_t enc_len,
- const u8 *auth, size_t auth_len)
+ const u8 *auth, size_t auth_len, int no_defer)
{
const char *msgtype = "push";
- struct os_time now;
- struct os_time tsend;
- const u8 *f_timestamp;
- size_t f_timestamp_len;
wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 push");
- RRB_GET_AUTH(FT_RRB_TIMESTAMP, timestamp, msgtype, sizeof(le32));
- tsend.sec = WPA_GET_LE32(f_timestamp);
- wpa_printf(MSG_DEBUG, "FT: timestamp=%ld", tsend.sec);
- os_get_time(&now);
- if ((now.sec > tsend.sec && now.sec - tsend.sec > 60) ||
- (now.sec < tsend.sec && tsend.sec - now.sec > 60)) {
- wpa_printf(MSG_DEBUG,
- "FT(RRB): push did not have a valid timestamp: sender time %ld own time %ld",
- tsend.sec, now.sec);
+ if (wpa_ft_rrb_rx_r1(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_PUSH,
+ enc, enc_len, auth, auth_len, msgtype, NULL,
+ no_defer ? NULL : wpa_ft_rrb_rx_push) < 0)
return -1;
+
+ return 0;
+}
+
+
+static int wpa_ft_rrb_rx_seq(struct wpa_authenticator *wpa_auth,
+ const u8 *src_addr, int type,
+ 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)
+{
+ struct ft_remote_r0kh *r0kh = NULL;
+ struct ft_remote_r1kh *r1kh = NULL;
+ const u8 *f_r0kh_id, *f_r1kh_id;
+ size_t f_r0kh_id_len, f_r1kh_id_len;
+ int to_r0kh, to_r1kh;
+ u8 *plain = NULL;
+ size_t plain_len = 0;
+
+ 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);
+
+ to_r0kh = !wpa_ft_rrb_check_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len);
+ to_r1kh = !wpa_ft_rrb_check_r1kh(wpa_auth, f_r1kh_id);
+
+ if (to_r0kh && to_r1kh) {
+ wpa_printf(MSG_DEBUG, "FT: seq - local R0KH-ID and R1KH-ID");
+ goto out;
}
- if (wpa_ft_rrb_rx_r1(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_PUSH,
- enc, enc_len, auth, auth_len, msgtype, NULL) < 0)
- return -1;
+ if (!to_r0kh && !to_r1kh) {
+ wpa_printf(MSG_DEBUG, "FT: seq - remote R0KH-ID and R1KH-ID");
+ goto out;
+ }
+
+ if (!to_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) {
+ wpa_hexdump(MSG_DEBUG, "FT: Did not find R0KH-ID",
+ f_r0kh_id, f_r0kh_id_len);
+ goto out;
+ }
+
+ *key = r0kh->key;
+ *key_len = sizeof(r0kh->key);
+ *rkh_seq = r0kh->seq;
+ }
+
+ if (!to_r1kh) {
+ wpa_ft_rrb_lookup_r1kh(wpa_auth, f_r1kh_id, &r1kh);
+ if (!r1kh || os_memcmp(r1kh->addr, src_addr, ETH_ALEN) != 0) {
+ wpa_hexdump(MSG_DEBUG, "FT: Did not find R1KH-ID",
+ f_r1kh_id, FT_R1KH_ID_LEN);
+ goto out;
+ }
+
+ *key = r1kh->key;
+ *key_len = sizeof(r1kh->key);
+ *rkh_seq = r1kh->seq;
+ }
+
+ if (wpa_ft_rrb_decrypt(*key, *key_len, enc, enc_len, auth, auth_len,
+ src_addr, type, &plain, &plain_len) < 0)
+ goto out;
+
+ os_free(plain);
+
+ return 0;
+out:
+ return -1;
+}
+
+
+static int wpa_ft_rrb_rx_seq_req(struct wpa_authenticator *wpa_auth,
+ const u8 *src_addr,
+ const u8 *enc, size_t enc_len,
+ const u8 *auth, size_t auth_len,
+ int no_defer)
+{
+ int ret = -1;
+ 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;
+ u8 *packet = NULL, *key = NULL;
+ size_t packet_len = 0, key_len = 0;
+ struct tlv_list seq_resp_auth[5];
+
+ wpa_printf(MSG_DEBUG, "FT: Received sequence number request");
+
+ 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)
+ goto out;
+
+ RRB_GET_AUTH(FT_RRB_NONCE, nonce, "seq request", FT_RRB_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: seq request - nonce", f_nonce, f_nonce_len);
+
+ 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);
+
+ if (wpa_ft_new_seq(rkh_seq, &f_seq) < 0) {
+ wpa_printf(MSG_DEBUG, "FT: Failed to get seq num");
+ goto out;
+ }
+
+ seq_resp_auth[0].type = FT_RRB_NONCE;
+ seq_resp_auth[0].len = f_nonce_len;
+ seq_resp_auth[0].data = f_nonce;
+ seq_resp_auth[1].type = FT_RRB_SEQ;
+ seq_resp_auth[1].len = sizeof(f_seq);
+ seq_resp_auth[1].data = (u8 *) &f_seq;
+ seq_resp_auth[2].type = FT_RRB_R0KH_ID;
+ seq_resp_auth[2].len = f_r0kh_id_len;
+ seq_resp_auth[2].data = f_r0kh_id;
+ seq_resp_auth[3].type = FT_RRB_R1KH_ID;
+ seq_resp_auth[3].len = FT_R1KH_ID_LEN;
+ seq_resp_auth[3].data = f_r1kh_id;
+ seq_resp_auth[4].type = FT_RRB_LAST_EMPTY;
+ seq_resp_auth[4].len = 0;
+ seq_resp_auth[4].data = NULL;
+
+ if (wpa_ft_rrb_build(key, key_len, NULL, NULL, seq_resp_auth,
+ wpa_auth->addr, FT_PACKET_R0KH_R1KH_SEQ_RESP,
+ &packet, &packet_len) < 0)
+ goto out;
+
+ wpa_ft_rrb_oui_send(wpa_auth, src_addr,
+ FT_PACKET_R0KH_R1KH_SEQ_RESP, packet,
+ packet_len);
+
+out:
+ os_free(packet);
+
+ return ret;
+}
+
+
+static int wpa_ft_rrb_rx_seq_resp(struct wpa_authenticator *wpa_auth,
+ const u8 *src_addr,
+ const u8 *enc, size_t enc_len,
+ const u8 *auth, size_t auth_len,
+ int no_defer)
+{
+ u8 *key = NULL;
+ size_t key_len = 0;
+ const u8 *f_nonce, *f_seq;
+ size_t f_nonce_len, f_seq_len;
+ struct ft_remote_seq *rkh_seq = NULL;
+ struct ft_remote_item *item;
+ struct os_reltime now, now_remote;
+ int seq_ret, found;
+ const struct ft_rrb_seq *msg_both;
+ u32 msg_dom, msg_seq;
+
+ wpa_printf(MSG_DEBUG, "FT: Received sequence number response");
+
+ 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)
+ 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);
+
+ found = 0;
+ dl_list_for_each(item, &rkh_seq->rx.queue, struct ft_remote_item,
+ list) {
+ if (os_memcmp_const(f_nonce, item->nonce,
+ FT_RRB_NONCE_LEN) != 0 ||
+ os_get_reltime(&now) < 0 ||
+ os_reltime_expired(&now, &item->nonce_ts, ftRRBseqTimeout))
+ continue;
+
+ found = 1;
+ break;
+ }
+ if (!found) {
+ wpa_printf(MSG_DEBUG, "FT: seq response - bad nonce");
+ goto out;
+ }
+
+ 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) {
+ wpa_printf(MSG_DEBUG, "FT: seq response - valid seq number");
+ wpa_ft_rrb_seq_accept(wpa_auth, rkh_seq, src_addr, auth,
+ auth_len, "seq response");
+ } else {
+ wpa_printf(MSG_DEBUG, "FT: seq response - reset seq number");
+
+ RRB_GET_AUTH(FT_RRB_SEQ, seq, "seq response",
+ sizeof(*msg_both));
+ msg_both = (const struct ft_rrb_seq *) f_seq;
+
+ msg_dom = le_to_host32(msg_both->dom);
+ msg_seq = le_to_host32(msg_both->seq);
+ now_remote.sec = le_to_host32(msg_both->ts);
+ now_remote.usec = 0;
+
+ rkh_seq->rx.num_last = 2;
+ rkh_seq->rx.dom = msg_dom;
+ rkh_seq->rx.offsetidx = 0;
+ /* Accept some older, possibly cached packets as well */
+ rkh_seq->rx.last[0] = msg_seq - FT_REMOTE_SEQ_BACKLOG -
+ dl_list_len(&rkh_seq->rx.queue);
+ rkh_seq->rx.last[1] = msg_seq;
+
+ /* local time - offset = remote time
+ * <=> local time - remote time = offset */
+ os_reltime_sub(&now, &now_remote, &rkh_seq->rx.time_offset);
+ }
+
+ wpa_ft_rrb_seq_flush(wpa_auth, rkh_seq, 1);
return 0;
out:
@@ -2369,13 +3004,24 @@ 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);
+ wpa_ft_rrb_rx_pull(wpa_auth, src_addr, enc, elen, auth, alen,
+ 0);
break;
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);
break;
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);
+ break;
+ case FT_PACKET_R0KH_R1KH_SEQ_REQ:
+ wpa_ft_rrb_rx_seq_req(wpa_auth, src_addr, enc, elen, auth, alen,
+ 0);
+ break;
+ case FT_PACKET_R0KH_R1KH_SEQ_RESP:
+ wpa_ft_rrb_rx_seq_resp(wpa_auth, src_addr, enc, elen, auth,
+ alen, 0);
break;
}
}
@@ -2386,10 +3032,9 @@ static int wpa_ft_generate_pmk_r1(struct wpa_authenticator *wpa_auth,
struct ft_remote_r1kh *r1kh,
const u8 *s1kh_id)
{
- struct os_time now;
u8 *packet;
size_t packet_len;
- u8 f_timestamp[sizeof(le32)];
+ struct ft_rrb_seq f_seq;
struct tlv_list push[] = {
{ .type = FT_RRB_S1KH_ID, .len = ETH_ALEN,
.data = s1kh_id },
@@ -2398,8 +3043,8 @@ static int wpa_ft_generate_pmk_r1(struct wpa_authenticator *wpa_auth,
{ .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL },
};
struct tlv_list push_auth[] = {
- { .type = FT_RRB_TIMESTAMP, .len = sizeof(f_timestamp),
- .data = f_timestamp },
+ { .type = FT_RRB_SEQ, .len = sizeof(f_seq),
+ .data = (u8 *) &f_seq },
{ .type = FT_RRB_R0KH_ID,
.len = wpa_auth->conf.r0_key_holder_len,
.data = wpa_auth->conf.r0_key_holder },
@@ -2408,8 +3053,10 @@ static int wpa_ft_generate_pmk_r1(struct wpa_authenticator *wpa_auth,
{ .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL },
};
- os_get_time(&now);
- WPA_PUT_LE32(f_timestamp, now.sec);
+ if (wpa_ft_new_seq(r1kh->seq, &f_seq) < 0) {
+ wpa_printf(MSG_DEBUG, "FT: Failed to get seq num");
+ return -1;
+ }
if (wpa_ft_rrb_build_r0(r1kh->key, sizeof(r1kh->key), push, pmk_r0,
r1kh->id, s1kh_id, push_auth, wpa_auth->addr,
@@ -2447,10 +3094,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));
- r1kh = wpa_auth->conf.r1kh_list;
- while (r1kh) {
+ for (r1kh = wpa_auth->conf.r1kh_list; r1kh; r1kh = r1kh->next) {
+ if (wpa_ft_rrb_init_r1kh_seq(r1kh) < 0)
+ continue;
wpa_ft_generate_pmk_r1(wpa_auth, r0, r1kh, addr);
- r1kh = r1kh->next;
}
}
diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
index 3e698e8..b364fce 100644
--- a/src/ap/wpa_auth_glue.c
+++ b/src/ap/wpa_auth_glue.c
@@ -578,6 +578,10 @@ static struct eth_p_oui_ctx * hostapd_wpa_get_oui(struct hostapd_data *hapd,
return hapd->oui_resp;
case FT_PACKET_R0KH_R1KH_PUSH:
return hapd->oui_push;
+ case FT_PACKET_R0KH_R1KH_SEQ_REQ:
+ return hapd->oui_sreq;
+ case FT_PACKET_R0KH_R1KH_SEQ_RESP:
+ return hapd->oui_sresp;
#endif /* CONFIG_IEEE80211R_AP */
default:
return NULL;
@@ -836,6 +840,18 @@ static int hostapd_wpa_register_ft_oui(struct hostapd_data *hapd,
if (!hapd->oui_push)
return -1;
+ hapd->oui_sreq = eth_p_oui_register(hapd, ft_iface,
+ FT_PACKET_R0KH_R1KH_SEQ_REQ,
+ hostapd_rrb_oui_receive, hapd);
+ if (!hapd->oui_sreq)
+ return -1;
+
+ hapd->oui_sresp = eth_p_oui_register(hapd, ft_iface,
+ FT_PACKET_R0KH_R1KH_SEQ_RESP,
+ hostapd_rrb_oui_receive, hapd);
+ if (!hapd->oui_sresp)
+ return -1;
+
return 0;
}
@@ -848,6 +864,10 @@ static void hostapd_wpa_unregister_ft_oui(struct hostapd_data *hapd)
hapd->oui_resp = NULL;
eth_p_oui_unregister(hapd->oui_push);
hapd->oui_push = NULL;
+ eth_p_oui_unregister(hapd->oui_sreq);
+ hapd->oui_sreq = NULL;
+ eth_p_oui_unregister(hapd->oui_sresp);
+ hapd->oui_sresp = NULL;
}
#endif /* CONFIG_IEEE80211R_AP */
diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h
index 3279ad4..aebf75c 100644
--- a/src/ap/wpa_auth_i.h
+++ b/src/ap/wpa_auth_i.h
@@ -9,6 +9,8 @@
#ifndef WPA_AUTH_I_H
#define WPA_AUTH_I_H
+#include "utils/list.h"
+
/* max(dot11RSNAConfigGroupUpdateCount,dot11RSNAConfigPairwiseUpdateCount) */
#define RSNA_MAX_EAPOL_RETRIES 4
@@ -211,6 +213,38 @@ struct wpa_authenticator {
};
+#ifdef CONFIG_IEEE80211R_AP
+
+#define FT_REMOTE_SEQ_BACKLOG 16
+struct ft_remote_seq_rx {
+ u32 dom;
+ struct os_reltime time_offset; /* local time - offset = remote time */
+
+ /* accepted sequence numbers: (offset ... offset + 0x40000000]
+ * (except those in last)
+ * dropped sequence numbers: (offset - 0x40000000 ... offset]
+ * all others trigger SEQ_REQ message (except first message)
+ */
+ u32 last[FT_REMOTE_SEQ_BACKLOG];
+ unsigned int num_last;
+ u32 offsetidx;
+
+ struct dl_list queue; /* send nonces + rrb msgs awaiting seq resp */
+};
+
+struct ft_remote_seq_tx {
+ u32 dom; /* non zero if initialized */
+ u32 seq;
+};
+
+struct ft_remote_seq {
+ struct ft_remote_seq_rx rx;
+ struct ft_remote_seq_tx tx;
+};
+
+#endif /* CONFIG_IEEE80211R_AP */
+
+
int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
const u8 *pmkid);
void wpa_auth_logger(struct wpa_authenticator *wpa_auth, const u8 *addr,