From patchwork Sun Apr 2 12:52:52 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: michael-dev X-Patchwork-Id: 746177 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from bombadil.infradead.org (bombadil.infradead.org [65.50.211.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3vwwDN55Spz9s0m for ; Sun, 2 Apr 2017 22:54:28 +1000 (AEST) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="FuMv2wQu"; dkim-atps=neutral DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:MIME-Version:Cc:List-Subscribe: List-Help:List-Post:List-Archive:List-Unsubscribe:List-Id:References: In-Reply-To:Message-Id:Date:Subject:To:From:Reply-To:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Owner; bh=8567uA8KvpPr60lLN0wDOOvTo3SqwE94girKJBFbm2Y=; b=FuMv2wQupFrFcikpGBGi8Ekmbb 2b4xujjOFHkqduy1Z0zI5HxPXFpZ94ONqQJaGHjaqzvQs41GQxK/WSQdagHKR+gl1HteekD8tfAR6 wTrBZ7lGhcn5Tt7R1WOqKhp2Q1Svt65YUXeBTQRFeTVPT5b1Z+Z7NLOAPg9MOCPXLS8rarBlqSP0J WGjyVvDW0HCBdsyx1dN8Gj9/oto4JAf3KpHmQbQfBWo7SAw9ABKk2ZtnznqO3/WQmVVV1ge6Xi9dq 9mB1re/d5b3ZN9yymlf2wZWxr5rBcFMkx6M9Ynbpujz5Iov1SLwt8i/c+qMjAqMmrA0MzLMwFf4s/ UX01M3YQ==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.87 #1 (Red Hat Linux)) id 1cuf1X-0004Pj-CU; Sun, 02 Apr 2017 12:54:27 +0000 Received: from mail.fem.tu-ilmenau.de ([141.24.220.54]) by bombadil.infradead.org with esmtp (Exim 4.87 #1 (Red Hat Linux)) id 1cuf1P-0003vY-D9 for hostap@lists.infradead.org; Sun, 02 Apr 2017 12:54:25 +0000 Received: from localhost (localhost [127.0.0.1]) by mail.fem.tu-ilmenau.de (Postfix) with ESMTP id 858A16B11; Sun, 2 Apr 2017 14:53:40 +0200 (CEST) X-Virus-Scanned: amavisd-new at fem.tu-ilmenau.de Received: from mail.fem.tu-ilmenau.de ([127.0.0.1]) by localhost (mail.fem.tu-ilmenau.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id TqrqRD+AwiG3; Sun, 2 Apr 2017 14:53:36 +0200 (CEST) Received: from mail-backup.fem.tu-ilmenau.de (mail-backup.net.fem.tu-ilmenau.de [10.42.40.22]) (using TLSv1 with cipher ADH-AES256-SHA (256/256 bits)) (No client certificate requested) by mail.fem.tu-ilmenau.de (Postfix) with ESMTPS; Sun, 2 Apr 2017 14:53:33 +0200 (CEST) Received: from a234.fem.tu-ilmenau.de (ray-controller.net.fem.tu-ilmenau.de [10.42.51.234]) by mail-backup.fem.tu-ilmenau.de (Postfix) with ESMTP id 73EE05604F; Sun, 2 Apr 2017 14:53:33 +0200 (CEST) Received: by a234.fem.tu-ilmenau.de (Postfix, from userid 1000) id 8A9A7306AC96; Sun, 2 Apr 2017 14:52:54 +0200 (CEST) From: Michael Braun To: hostap@lists.infradead.org Subject: [PATCHv6 4/5] FT: add support for wildcard R0KH / R1KH Date: Sun, 2 Apr 2017 14:52:52 +0200 Message-Id: <1491137573-643-5-git-send-email-michael-dev@fami-braun.de> X-Mailer: git-send-email 2.1.4 In-Reply-To: <1491137573-643-1-git-send-email-michael-dev@fami-braun.de> References: <1491137573-643-1-git-send-email-michael-dev@fami-braun.de> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20170402_055420_617934_A2CDFB80 X-CRM114-Status: GOOD ( 18.76 ) X-Spam-Score: -4.2 (----) X-Spam-Report: SpamAssassin version 3.4.1 on bombadil.infradead.org summary: Content analysis details: (-4.2 points) pts rule name description ---- ---------------------- -------------------------------------------------- -2.3 RCVD_IN_DNSWL_MED RBL: Sender listed at http://www.dnswl.org/, medium trust [141.24.220.54 listed in list.dnswl.org] -1.9 BAYES_00 BODY: Bayes spam probability is 0 to 1% [score: 0.0000] X-BeenThere: hostap@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: projekt-wlan@fem.tu-ilmenau.de, Michael Braun MIME-Version: 1.0 Sender: "Hostap" Errors-To: hostap-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org Enable using FT RRB without configuring each other AP locally. Instead, broadcast messages are exchanged. When an R0KH or R1KH is discovered, it is cached for 1d. When a station uses an invalid or offline r0kh_id, requests are always broadcasted. In order to avoid this, if r0kh does not reply, a temporary blacklist entry is added to r0kh_list. In order 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. In order to free newly created stations later, the r*kh_list start pointer in conf needs to be updateable from wpa_auto_ft.c, where only wconf is accessed. Signed-off-by: Michael Braun --- 2017-04-02: - add_r1kh in rx_seq only after decrypt passes v5: squash wildcard test patch changes to wpa_auth_ft.c here add debug output for pull req timeout v4: squash wildcard, positive and negative caching into one patch for proper sequence number implementation v3: merge retransmission into this patch and update comment --- hostapd/config_file.c | 8 + hostapd/hostapd.conf | 23 +++ src/ap/ap_config.c | 4 + src/ap/ap_config.h | 4 + src/ap/wpa_auth.c | 3 + src/ap/wpa_auth.h | 12 +- src/ap/wpa_auth_ft.c | 515 ++++++++++++++++++++++++++++++++++++++++++------- src/ap/wpa_auth_glue.c | 8 +- src/ap/wpa_auth_i.h | 1 + 9 files changed, 508 insertions(+), 70 deletions(-) diff --git a/hostapd/config_file.c b/hostapd/config_file.c index 2070a74..8435a36 100644 --- a/hostapd/config_file.c +++ b/hostapd/config_file.c @@ -2636,6 +2636,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'", diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf index 7e61d1c..6f8432a 100644 --- a/hostapd/hostapd.conf +++ b/hostapd/hostapd.conf @@ -1459,6 +1459,10 @@ 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 broadcasted. +# 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: <256-bit key as hex string> @@ -1468,6 +1472,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 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 = 1d) + +# Timeout (milli seconds) for requesting PMK-R1 from R0KH using PULL request +# and number of retries. +#rkh_pull_timeout = 1000 (default = 1s) +#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 = 86400 # Whether PMK-R1 push is enabled at R0KH # 0 = do not push PMK-R1 to all configured R1KHs (default) diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c index 10e1c4b..b889a3c 100644 --- a/src/ap/ap_config.c +++ b/src/ap/ap_config.c @@ -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; diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h index 989b071..0d5274a 100644 --- a/src/ap/ap_config.h +++ b/src/ap/ap_config.h @@ -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; diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c index 338c6c7..b68b94e 100644 --- a/src/ap/wpa_auth.c +++ b/src/ap/wpa_auth.c @@ -704,6 +704,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(). */ diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h index 49c0480..3e08d81 100644 --- a/src/ap/wpa_auth.h +++ b/src/ap/wpa_auth.h @@ -100,7 +100,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: @@ -182,9 +183,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; @@ -367,6 +372,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); diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c index ba380ab..d3b02cd 100644 --- a/src/ap/wpa_auth_ft.c +++ b/src/ap/wpa_auth_ft.c @@ -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 { @@ -1004,22 +1006,26 @@ 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 = NULL; + *r0kh_wildcard = NULL; *r0kh_out = NULL; - r0kh = wpa_auth->conf.r0kh_list; + if (wpa_auth->conf.r0kh_list) + r0kh = *wpa_auth->conf.r0kh_list; + 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) @@ -1047,23 +1053,26 @@ 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 = NULL; + *r1kh_wildcard = NULL; *r1kh_out = NULL; - r1kh = wpa_auth->conf.r1kh_list; + if (wpa_auth->conf.r1kh_list) + r1kh = *wpa_auth->conf.r1kh_list; 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 +1103,164 @@ 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; + + r0kh = *wpa_auth->conf.r0kh_list; + while (r0kh) { + if (r0kh != timeout_ctx) { + r0kh = r0kh->next; + continue; + } + 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); + break; + } +} + + +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 == NULL) + return NULL; + + 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; + + r1kh = *wpa_auth->conf.r1kh_list; + while (r1kh) { + if (r1kh != timeout_ctx) { + r1kh = r1kh->next; + continue; + } + 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); + break; + } +} + + +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 == NULL) + 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); +} + + void wpa_ft_deinit_seq_timeout(struct wpa_authenticator *wpa_auth) { struct ft_remote_r0kh *r0kh; @@ -1101,14 +1268,18 @@ void wpa_ft_deinit_seq_timeout(struct wpa_authenticator *wpa_auth) eloop_cancel_timeout(wpa_ft_rrb_seq_timeout, wpa_auth, ELOOP_ALL_CTX); - r0kh = wpa_auth->conf.r0kh_list; + if (!wpa_auth->conf.r0kh_list) + return; + r0kh = *wpa_auth->conf.r0kh_list; while (r0kh) { if (r0kh->seq) wpa_ft_rrb_seq_flush(wpa_auth, r0kh->seq, 0); r0kh = r0kh->next; } - r1kh = wpa_auth->conf.r1kh_list; + if (!wpa_auth->conf.r1kh_list) + return; + r1kh = *wpa_auth->conf.r1kh_list; while (r1kh) { if (r1kh->seq) wpa_ft_rrb_seq_flush(wpa_auth, r1kh->seq, 0); @@ -1120,6 +1291,52 @@ void wpa_ft_deinit_seq_timeout(struct wpa_authenticator *wpa_auth) void wpa_ft_deinit(struct wpa_authenticator *wpa_auth) { wpa_ft_deinit_seq_timeout(wpa_auth); + eloop_cancel_timeout(wpa_ft_rrb_del_r1kh, wpa_auth, ELOOP_ALL_CTX); + eloop_cancel_timeout(wpa_ft_rrb_del_r0kh, wpa_auth, ELOOP_ALL_CTX); +} + + +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; + const u8 *zaddr = (u8 *) "\x00\x00\x00\x00\x00\x00"; + + 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_memcpy(r0kh->addr, zaddr, ETH_ALEN); + } else + wpa_ft_rrb_add_r0kh(wpa_auth, r0kh_wildcard, zaddr, 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); } @@ -1127,19 +1344,46 @@ 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; + + 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 list for seq num check */ + const int 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); @@ -1154,7 +1398,8 @@ static int wpa_ft_pull_pmk_r1(struct wpa_state_machine *sm, 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) { + 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; @@ -1190,11 +1435,16 @@ 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 == NULL) 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); @@ -1809,7 +2059,8 @@ static int wpa_ft_process_auth_req(struct wpa_state_machine *sm, 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"); + "PMK-R1 and either unknown or blocked " + "R0KH-ID or NAK from R0KH"); return WLAN_STATUS_INVALID_PMKID; } @@ -1900,6 +2151,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) { @@ -2181,6 +2433,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) { @@ -2259,6 +2512,10 @@ static int wpa_ft_rrb_build_r0(const u8 *key, const size_t key_len, u8 f_pairwise[sizeof(le16)]; int ret; + 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; @@ -2296,7 +2553,7 @@ static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth, const int type = FT_PACKET_R0KH_R1KH_PULL; 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 = NULL; size_t key_len = 0; int seq_ret; @@ -2321,17 +2578,29 @@ 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 == NULL || 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, noDefer); + 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, noDefer); + if (!noDefer && 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; @@ -2339,6 +2608,13 @@ static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth, src_addr, type, &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, @@ -2349,6 +2625,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, @@ -2379,10 +2657,8 @@ static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth, { .type = FT_RRB_LAST_EMPTY, .len = 0, .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, @@ -2404,6 +2680,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, const u8 type, @@ -2418,7 +2695,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; @@ -2440,14 +2717,28 @@ 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 == NULL || 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 = wpa_ft_rrb_seq_chk(r0kh->seq, src_addr, enc, enc_len, auth, - auth_len, msgtype, cb ? 0 : 1); + 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; + if (seq_ret == FT_RRB_SEQ_DROP) goto out; @@ -2455,6 +2746,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, @@ -2464,6 +2762,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)); @@ -2471,9 +2771,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); @@ -2506,13 +2808,20 @@ static void ft_finish_pull(struct wpa_state_machine *sm) size_t resp_ies_len; u16 status; + if (sm->ft_pending_cb == NULL || sm->ft_pending_req_ies == NULL) + 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); @@ -2555,7 +2864,7 @@ static int wpa_ft_rrb_rx_resp(struct wpa_authenticator *wpa_auth, { const char *msgtype = "pull response"; const int type = FT_PACKET_R0KH_R1KH_RESP; - int ret = -1; + int nak, ret = -1; struct ft_get_sta_ctx ctx; u8 s1kh_id[ETH_ALEN]; const u8 *f_nonce; @@ -2577,6 +2886,11 @@ static int wpa_ft_rrb_rx_resp(struct wpa_authenticator *wpa_auth, ret = wpa_ft_rrb_rx_r1(wpa_auth, src_addr, type, enc, enc_len, auth, auth_len, msgtype, s1kh_id, noDefer ? NULL : &wpa_ft_rrb_rx_resp); + if (ret == -2) { + ret = 0; + nak = 1; + } else + nak = 0; if (ret < 0) return -1; @@ -2584,6 +2898,9 @@ static int wpa_ft_rrb_rx_resp(struct wpa_authenticator *wpa_auth, if (wpa_auth_for_each_sta(wpa_auth, ft_get_sta_cb, &ctx)) { 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); } @@ -2617,7 +2934,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; @@ -2626,6 +2947,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); @@ -2645,31 +2968,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 == NULL || - 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 == NULL || - 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, @@ -2678,6 +3008,38 @@ static int wpa_ft_rrb_rx_seq(struct wpa_authenticator *wpa_auth, os_free(plain); plain = NULL; + 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; @@ -2693,7 +3055,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; @@ -2701,7 +3063,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); @@ -2752,6 +3114,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; @@ -2765,7 +3129,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); @@ -2788,6 +3153,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) { @@ -2952,6 +3331,7 @@ void wpa_ft_rrb_oui_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr, const u16 *head; const u8 *auth, *enc; size_t alen, elen; + int noDefer = 0; wpa_printf(MSG_DEBUG, "FT: RRB-OUI received frame from remote AP " MACSTR, MAC2STR(src_addr)); @@ -2969,7 +3349,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; + noDefer = 1; } head = (u16 *) data; @@ -2991,23 +3371,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); + noDefer); break; case FT_PACKET_R0KH_R1KH_RESP: wpa_ft_rrb_rx_resp(wpa_auth, src_addr, enc, elen, auth, alen, - 0); + noDefer); break; case FT_PACKET_R0KH_R1KH_PUSH: wpa_ft_rrb_rx_push(wpa_auth, src_addr, enc, elen, auth, alen, - 0); + noDefer); break; case FT_PACKET_R0KH_R1KH_SEQ_REQ: wpa_ft_rrb_rx_seq_req(wpa_auth, src_addr, enc, elen, auth, alen, - 0); + noDefer); break; case FT_PACKET_R0KH_R1KH_SEQ_RESP: wpa_ft_rrb_rx_seq_resp(wpa_auth, src_addr, enc, elen, auth, - alen, 0); + alen, noDefer); break; } } @@ -3067,6 +3447,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) { @@ -3082,7 +3464,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); diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c index ba964e6..1e4ef37 100644 --- a/src/ap/wpa_auth_glue.c +++ b/src/ap/wpa_auth_glue.c @@ -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; diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h index 4a9c629..3e6c6a1 100644 --- a/src/ap/wpa_auth_i.h +++ b/src/ap/wpa_auth_i.h @@ -124,6 +124,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;