]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
FT: Add support for wildcard R0KH/R1KH
authorMichael Braun <michael-dev@fami-braun.de>
Sun, 2 Apr 2017 12:52:52 +0000 (14:52 +0200)
committerJouni Malinen <j@w1.fi>
Wed, 3 May 2017 19:16:14 +0000 (22:16 +0300)
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>
hostapd/config_file.c
hostapd/hostapd.conf
src/ap/ap_config.c
src/ap/ap_config.h
src/ap/wpa_auth.c
src/ap/wpa_auth.h
src/ap/wpa_auth_ft.c
src/ap/wpa_auth_glue.c
src/ap/wpa_auth_i.h

index 2eb72055fd18336e086d2908ab6a4f88589e5f3d..900d81131ea630afb71693dc6cceb67d4f96f898 100644 (file)
@@ -2641,6 +2641,14 @@ static int hostapd_config_fill(struct hostapd_config *conf,
                bss->r0_key_lifetime = atoi(pos);
        } else if (os_strcmp(buf, "reassociation_deadline") == 0) {
                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) {
                if (add_r0kh(bss, pos) < 0) {
                        wpa_printf(MSG_DEBUG, "Line %d: Invalid r0kh '%s'",
index c5ba0e9cd64c83cc632c7d93ca03f647521f85c4..135715eb4a0153096e625916ee5d11115398a5e0 100644 (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:06 r0kh-2.example.com 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff
 # 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
 # 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:06 02:11:22:33:44:66 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff
 # 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
 # format was changed. That shorter key length is still supported for backwards
index 6b3d4e86299170a25de011533b3c6b305a8dee09..09cbec3b337bb47b58fce82b5aa55e1b857f64b1 100644 (file)
@@ -93,6 +93,10 @@ void hostapd_config_defaults_bss(struct hostapd_bss_config *bss)
 
 #ifdef CONFIG_IEEE80211R_AP
        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 */
 
        bss->radius_das_time_window = 300;
index 498891c5471d649def8538745eb95b0ea3ba2e20..849c69d92af4a488aaeeed7e2322c94da428e004 100644 (file)
@@ -342,6 +342,10 @@ struct hostapd_bss_config {
        u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN];
        u8 r1_key_holder[FT_R1KH_ID_LEN];
        u32 r0_key_lifetime;
+       int rkh_pos_timeout;
+       int rkh_neg_timeout;
+       int rkh_pull_timeout; /* ms */
+       int rkh_pull_retries;
        u32 reassociation_deadline;
        struct ft_remote_r0kh *r0kh_list;
        struct ft_remote_r1kh *r1kh_list;
index 9fceca65d477863c4e16990a8cd36b74dcfb8da1..1f5885eeabbcd735d57c1ef0940af4fa0ac86e91 100644 (file)
@@ -706,6 +706,9 @@ void wpa_auth_sta_deinit(struct wpa_state_machine *sm)
        sm->pending_1_of_4_timeout = 0;
        eloop_cancel_timeout(wpa_sm_call_step, sm, NULL);
        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) {
                /* Must not free state machine while wpa_sm_step() is running.
                 * Freeing will be completed in the end of wpa_sm_step(). */
index bab56191b8b2a5385050dcecfa9fa43fb204aab2..5f8bb8726529484b323b51491c20321c5fc1a3ce 100644 (file)
@@ -103,7 +103,8 @@ struct ft_rrb_seq {
  *   auth:
  *     required: SEQ, NONCE, R0KH_ID, R1KH_ID
  *   encrypted:
- *     required: S1KH_ID, session TLVs
+ *     required: S1KH_ID
+ *     optional: session TLVs
  *
  * push frame TLVs:
  *   auth:
@@ -185,9 +186,13 @@ struct wpa_auth_config {
        size_t r0_key_holder_len;
        u8 r1_key_holder[FT_R1KH_ID_LEN];
        u32 r0_key_lifetime;
+       int rkh_pos_timeout;
+       int rkh_neg_timeout;
+       int rkh_pull_timeout; /* ms */
+       int rkh_pull_retries;
        u32 reassociation_deadline;
-       struct ft_remote_r0kh *r0kh_list;
-       struct ft_remote_r1kh *r1kh_list;
+       struct ft_remote_r0kh **r0kh_list;
+       struct ft_remote_r1kh **r1kh_list;
        int pmk_r1_push;
        int ft_over_ds;
        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);
 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_sta_deinit(struct wpa_state_machine *sm);
 #endif /* CONFIG_IEEE80211R_AP */
 
 void wpa_wnmsleep_rekey_gtk(struct wpa_state_machine *sm);
index 9db9b26c6548aef31319730be0c9769bdbe16183..b73ea9944c8e0aec7415b4c8917577019bca5494 100644 (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,
                                     u16 status, const u8 *resp_ies,
                                     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);
 
 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,
                                   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;
 
+       *r0kh_wildcard = 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 &&
-                   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;
-                       break;
-               }
        }
 
-       if (!*r0kh_out)
+       if (!*r0kh_out && !*r0kh_wildcard)
                wpa_printf(MSG_DEBUG, "FT: No matching R0KH found");
 
        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,
                                   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;
 
+       *r1kh_wildcard = 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 &&
-                   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;
-                       break;
-               }
        }
 
-       if (!*r1kh_out)
+       if (!*r1kh_out && !*r1kh_wildcard)
                wpa_printf(MSG_DEBUG, "FT: No matching R1KH found");
 
        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)
 {
        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);
 
-       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)
                        continue;
                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;
        }
 
-       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)
                        continue;
                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)
 {
        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 *pmk_r0_name)
 {
-       struct ft_remote_r0kh *r0kh;
+       struct ft_remote_r0kh *r0kh, *r0kh_wildcard;
        u8 *packet = NULL;
        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;
+       int tsecs, tusecs, first;
+       struct wpabuf *ft_pending_req_ies;
+       int r0kh_timeout;
        struct tlv_list req_enc[] = {
                { .type = FT_RRB_PMK_R0_NAME, .len = WPA_PMK_NAME_LEN,
                  .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 },
        };
 
+       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,
-                              &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) {
                wpa_hexdump(MSG_DEBUG, "FT: Did not find R0KH-ID",
                            sm->r0kh_id, sm->r0kh_id_len);
                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_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);
        }
 
-       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 "
                           "nonce");
                return -1;
@@ -1191,13 +1491,18 @@ static int wpa_ft_pull_pmk_r1(struct wpa_state_machine *sm,
                             &packet, &packet_len) < 0)
                return -1;
 
+       ft_pending_req_ies = wpabuf_alloc_copy(ies, ies_len);
        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) {
                os_free(packet);
                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,
                            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,
                                       pmk_r1, &pairwise) < 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");
+                       wpa_printf(MSG_DEBUG,
+                                  "FT: Did not have matching PMK-R1 and either unknown or blocked R0KH-ID or NAK from R0KH");
                        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_ctx = ctx;
        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,
                                      &resp_ies_len);
        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_ctx = sm;
        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,
                                      &resp_ies_len);
        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 },
        };
 
+       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,
                              s1kh_id, pmk_r1, pmk_r1_name) < 0)
                return -1;
@@ -2301,7 +2612,7 @@ static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth,
        const char *msgtype = "pull request";
        u8 *plain = NULL, *packet = NULL;
        size_t plain_len = 0, packet_len = 0;
-       struct ft_remote_r1kh *r1kh;
+       struct ft_remote_r1kh *r1kh, *r1kh_wildcard;
        const u8 *key;
        size_t key_len;
        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);
        wpa_printf(MSG_DEBUG, "FT: R1KH-ID=" MACSTR, MAC2STR(f_r1kh_id));
 
-       wpa_ft_rrb_lookup_r1kh(wpa_auth, f_r1kh_id, &r1kh);
-       if (!r1kh || os_memcmp(r1kh->addr, src_addr, ETH_ALEN) != 0)
+       wpa_ft_rrb_lookup_r1kh(wpa_auth, f_r1kh_id, &r1kh, &r1kh_wildcard);
+       if (r1kh) {
+               key = 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;
-       key = r1kh->key;
-       key_len = sizeof(r1kh->key);
+       }
 
        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);
+       seq_ret = FT_RRB_SEQ_DROP;
+       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)
                goto out;
 
@@ -2346,6 +2671,13 @@ static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth,
                               &plain, &plain_len) < 0)
                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) {
                wpa_ft_rrb_seq_req(wpa_auth, r1kh->seq, src_addr, f_r0kh_id,
                                   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,
                              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);
        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].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");
-               goto out;
-       }
 
        ret = wpa_ft_rrb_build_r0(key, key_len, resp, r0, f_r1kh_id, f_s1kh_id,
                                  resp_auth, wpa_auth->addr,
@@ -2417,6 +2749,7 @@ out:
 
 /* @returns  0 on success
  *          -1 on error
+ *          -2 if FR_RRB_PAIRWISE is missing
  */
 static int wpa_ft_rrb_rx_r1(struct wpa_authenticator *wpa_auth,
                            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;
        size_t plain_len = 0;
-       struct ft_remote_r0kh *r0kh;
+       struct ft_remote_r0kh *r0kh, *r0kh_wildcard;
        const u8 *key;
        size_t key_len;
        int seq_ret;
@@ -2453,14 +2786,31 @@ static int wpa_ft_rrb_rx_r1(struct wpa_authenticator *wpa_auth,
                goto out;
        }
 
-       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_ft_rrb_lookup_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len, &r0kh,
+                              &r0kh_wildcard);
+       if (r0kh) {
+               key = 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;
-       key = r0kh->key;
-       key_len = sizeof(r0kh->key);
+       }
+
+       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)
                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)
                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) {
                wpa_ft_rrb_seq_req(wpa_auth, r0kh->seq, src_addr, f_r0kh_id,
                                   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,
                              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);
        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)
                os_memcpy(s1kh_id_out, f_s1kh_id, ETH_ALEN);
 
+       ret = -2;
        RRB_GET(FT_RRB_PAIRWISE, pairwise, msgtype, sizeof(le16));
        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);
        wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name",
                    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;
        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),
                                      wpabuf_len(sm->ft_pending_req_ies),
                                      &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);
        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);
@@ -2568,7 +2936,7 @@ static int wpa_ft_rrb_rx_resp(struct wpa_authenticator *wpa_auth,
                              int no_defer)
 {
        const char *msgtype = "pull response";
-       int ret = -1;
+       int nak, ret = -1;
        struct ft_get_sta_ctx ctx;
        u8 s1kh_id[ETH_ALEN];
        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,
                               enc, enc_len, auth, auth_len, msgtype, s1kh_id,
                               no_defer ? NULL : &wpa_ft_rrb_rx_resp);
+       if (ret == -2) {
+               ret = 0;
+               nak = 1;
+       } else {
+               nak = 0;
+       }
        if (ret < 0)
                return -1;
 
@@ -2598,6 +2972,9 @@ static int wpa_ft_rrb_rx_resp(struct wpa_authenticator *wpa_auth,
                wpa_printf(MSG_DEBUG,
                           "FT: Response to a pending pull request for " MACSTR,
                           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);
        }
 
@@ -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 *auth, size_t auth_len,
                             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_r1kh *r1kh = NULL;
@@ -2638,6 +3019,8 @@ static int wpa_ft_rrb_rx_seq(struct wpa_authenticator *wpa_auth,
        int to_r0kh, to_r1kh;
        u8 *plain = NULL;
        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_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) {
                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, &r0kh_wildcard);
+               if (!r0kh_wildcard &&
+                   (!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 (r0kh) {
+                       *key = r0kh->key;
+                       *key_len = sizeof(r0kh->key);
+               } else {
+                       *key = r0kh_wildcard->key;
+                       *key_len = sizeof(r0kh_wildcard->key);
+               }
        }
 
        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_ft_rrb_lookup_r1kh(wpa_auth, f_r1kh_id, &r1kh,
+                                      &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",
                                    f_r1kh_id, FT_R1KH_ID_LEN);
                        goto out;
                }
-
-               *key = r1kh->key;
-               *key_len = sizeof(r1kh->key);
-               *rkh_seq = r1kh->seq;
+               if (r1kh) {
+                       *key = r1kh->key;
+                       *key_len = sizeof(r1kh->key);
+               } 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,
@@ -2688,6 +3080,39 @@ static int wpa_ft_rrb_rx_seq(struct wpa_authenticator *wpa_auth,
 
        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;
 out:
        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;
        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;
+       struct ft_remote_seq *rkh_seq = NULL;
        u8 *packet = NULL, *key = NULL;
        size_t packet_len = 0, key_len = 0;
        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,
                              enc, enc_len, auth, auth_len, &rkh_seq, &key,
-                             &key_len) < 0)
+                             &key_len, NULL, NULL, NULL, NULL) < 0)
                goto out;
 
        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;
        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;
        size_t f_nonce_len, f_seq_len;
        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,
                              enc, enc_len, auth, auth_len, &rkh_seq, &key,
-                             &key_len) < 0)
+                             &key_len, &r0kh, &r1kh, &r0kh_wildcard,
+                             &r1kh_wildcard) < 0)
                goto out;
 
        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;
        }
 
+       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,
                                     auth_len, "seq response", 1);
        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;
        size_t alen, elen;
+       int no_defer = 0;
 
        wpa_printf(MSG_DEBUG, "FT: RRB-OUI received frame from remote AP "
                   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
                           " to multicast address " MACSTR,
                           MAC2STR(src_addr), MAC2STR(dst_addr));
-               return;
+               no_defer = 1;
        }
 
        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) {
        case FT_PACKET_R0KH_R1KH_PULL:
                wpa_ft_rrb_rx_pull(wpa_auth, src_addr, enc, elen, auth, alen,
-                                  0);
+                                  no_defer);
                break;
        case FT_PACKET_R0KH_R1KH_RESP:
                wpa_ft_rrb_rx_resp(wpa_auth, src_addr, enc, elen, auth, alen,
-                                  0);
+                                  no_defer);
                break;
        case FT_PACKET_R0KH_R1KH_PUSH:
                wpa_ft_rrb_rx_push(wpa_auth, src_addr, enc, elen, auth, alen,
-                                  0);
+                                  no_defer);
                break;
        case FT_PACKET_R0KH_R1KH_SEQ_REQ:
                wpa_ft_rrb_rx_seq_req(wpa_auth, src_addr, enc, elen, auth, alen,
-                                     0);
+                                     no_defer);
                break;
        case FT_PACKET_R0KH_R1KH_SEQ_RESP:
                wpa_ft_rrb_rx_seq_resp(wpa_auth, src_addr, enc, elen, auth,
-                                      alen, 0);
+                                      alen, no_defer);
                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)
                return;
+       if (!wpa_auth->conf.r1kh_list)
+               return;
 
        r0 = wpa_auth->ft_pmk_cache->pmk_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 "
                   "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)
                        continue;
                wpa_ft_generate_pmk_r1(wpa_auth, r0, r1kh, addr);
index b364fcee695c773901fbbeb9775717ebb5da8b46..316f5d50b7484f4cae7fe4794712e0723f5326de 100644 (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);
        wconf->r0_key_lifetime = conf->r0_key_lifetime;
        wconf->reassociation_deadline = conf->reassociation_deadline;
-       wconf->r0kh_list = conf->r0kh_list;
-       wconf->r1kh_list = conf->r1kh_list;
+       wconf->rkh_pos_timeout = conf->rkh_pos_timeout;
+       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->ft_over_ds = conf->ft_over_ds;
        wconf->ft_psk_generate_local = conf->ft_psk_generate_local;
index aebf75c1f60c88a7f68a0407cf4071f950e77b5d..7ef817f542f0dbb936b6307d4e113523c8a778c0 100644 (file)
@@ -126,6 +126,7 @@ struct wpa_state_machine {
        u8 ft_pending_pull_nonce[FT_RRB_NONCE_LEN];
        u8 ft_pending_auth_transaction;
        u8 ft_pending_current_ap[ETH_ALEN];
+       int ft_pending_pull_left_retries;
 #endif /* CONFIG_IEEE80211R_AP */
 
        int pending_1_of_4_timeout;