From 1764559eef3020afebb427d48799c13514f45de0 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Sun, 12 Mar 2017 22:40:56 +0200 Subject: [PATCH] FILS: Add FILS SK auth PFS support in AP mode This adds an option to configure hostapd to enable use of perfect forward secrecy option in FILS shared key authentication. A new build option CONFIG_FILS_SK_PFS=y can be used to include this functionality. A new runtime configuration parameter fils_dh_group is used to enable this by specifying which DH group to use. For example, fils_dh_group=19 would allow FILS SK PFS to be used with a 256-bit random ECP group. Signed-off-by: Jouni Malinen --- hostapd/Android.mk | 4 ++ hostapd/Makefile | 4 ++ hostapd/config_file.c | 2 + hostapd/defconfig | 2 + hostapd/hostapd.conf | 5 ++ src/ap/ap_config.h | 1 + src/ap/ieee802_11.c | 132 +++++++++++++++++++++++++++++++++---- src/ap/ieee802_11_shared.c | 5 +- src/ap/sta_info.c | 4 ++ src/ap/sta_info.h | 4 ++ 10 files changed, 149 insertions(+), 14 deletions(-) diff --git a/hostapd/Android.mk b/hostapd/Android.mk index 10018bcea..b1940f802 100644 --- a/hostapd/Android.mk +++ b/hostapd/Android.mk @@ -272,6 +272,10 @@ L_CFLAGS += -DCONFIG_FILS OBJS += src/ap/fils_hlp.c NEED_SHA384=y NEED_AES_SIV=y +ifdef CONFIG_FILS_SK_PFS +L_CFLAGS += -DCONFIG_FILS_SK_PFS +NEED_ECC=y +endif endif ifdef CONFIG_WNM diff --git a/hostapd/Makefile b/hostapd/Makefile index 34a0e68a1..c443618ef 100644 --- a/hostapd/Makefile +++ b/hostapd/Makefile @@ -316,6 +316,10 @@ CFLAGS += -DCONFIG_FILS OBJS += ../src/ap/fils_hlp.o NEED_SHA384=y NEED_AES_SIV=y +ifdef CONFIG_FILS_SK_PFS +CFLAGS += -DCONFIG_FILS_SK_PFS +NEED_ECC=y +endif endif ifdef CONFIG_WNM diff --git a/hostapd/config_file.c b/hostapd/config_file.c index 2e4433d07..7b4380605 100644 --- a/hostapd/config_file.c +++ b/hostapd/config_file.c @@ -3658,6 +3658,8 @@ static int hostapd_config_fill(struct hostapd_config *conf, } else if (os_strcmp(buf, "fils_realm") == 0) { if (parse_fils_realm(bss, pos) < 0) return 1; + } else if (os_strcmp(buf, "fils_dh_group") == 0) { + bss->fils_dh_group = atoi(pos); } else if (os_strcmp(buf, "dhcp_server") == 0) { if (hostapd_parse_ip_addr(pos, &bss->dhcp_server)) { wpa_printf(MSG_ERROR, diff --git a/hostapd/defconfig b/hostapd/defconfig index 2370fc12c..521d877eb 100644 --- a/hostapd/defconfig +++ b/hostapd/defconfig @@ -358,6 +358,8 @@ CONFIG_IPV6=y # Note: This is an experimental and not yet complete implementation. This # should not be enabled for production use. #CONFIG_FILS=y +# FILS shared key authentication with PFS +#CONFIG_FILS_SK_PFS=y # Include internal line edit mode in hostapd_cli. This can be used to provide # limited command line editing and history support. diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf index 19596ce00..18c330bab 100644 --- a/hostapd/hostapd.conf +++ b/hostapd/hostapd.conf @@ -1393,6 +1393,11 @@ own_ip_addr=127.0.0.1 #fils_realm=example.com #fils_realm=example.org +# FILS DH Group for PFS +# 0 = PFS disabled with FILS shared key authentication (default) +# 1-65535 DH Group to use for FILS PFS +#fils_dh_group=0 + # DHCP server for FILS HLP # If configured, hostapd will act as a DHCP relay for all FILS HLP requests # that include a DHCPDISCOVER message and send them to the specific DHCP diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h index 539433907..989b07107 100644 --- a/src/ap/ap_config.h +++ b/src/ap/ap_config.h @@ -610,6 +610,7 @@ struct hostapd_bss_config { u8 fils_cache_id[FILS_CACHE_ID_LEN]; int fils_cache_id_set; struct dl_list fils_realms; /* list of struct fils_realm */ + int fils_dh_group; struct hostapd_ip_addr dhcp_server; int dhcp_rapid_commit_proxy; unsigned int fils_hlp_wait_time; diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c index 5bbd585fb..ec7332957 100644 --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c @@ -1014,8 +1014,9 @@ static void handle_auth_fils_finish(struct hostapd_data *hapd, const u8 *msk, size_t msk_len); static void handle_auth_fils(struct hostapd_data *hapd, struct sta_info *sta, - const struct ieee80211_mgmt *mgmt, size_t len, - u16 auth_transaction, u16 status_code) + const struct ieee80211_mgmt *mgmt, size_t len, + u16 auth_alg, u16 auth_transaction, + u16 status_code) { u16 resp = WLAN_STATUS_SUCCESS; const u8 *pos, *end; @@ -1033,8 +1034,76 @@ static void handle_auth_fils(struct hostapd_data *hapd, struct sta_info *sta, wpa_hexdump(MSG_DEBUG, "FILS: Authentication frame fields", pos, end - pos); - /* TODO: Finite Cyclic Group when using PK or PFS */ - /* TODO: Element when using PK or PFS */ + /* TODO: FILS PK */ +#ifdef CONFIG_FILS_SK_PFS + if (auth_alg == WLAN_AUTH_FILS_SK_PFS) { + u16 group; + struct wpabuf *pub; + size_t elem_len; + + /* Using FILS PFS */ + + /* Finite Cyclic Group */ + if (end - pos < 2) { + wpa_printf(MSG_DEBUG, + "FILS: No room for Finite Cyclic Group"); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + group = WPA_GET_LE16(pos); + pos += 2; + if (group != hapd->conf->fils_dh_group) { + wpa_printf(MSG_DEBUG, + "FILS: Unsupported Finite Cyclic Group: %u (expected %u)", + group, hapd->conf->fils_dh_group); + resp = WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; + goto fail; + } + + crypto_ecdh_deinit(sta->fils_ecdh); + sta->fils_ecdh = crypto_ecdh_init(group); + if (!sta->fils_ecdh) { + wpa_printf(MSG_INFO, + "FILS: Could not initialize ECDH with group %d", + group); + resp = WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; + goto fail; + } + + pub = crypto_ecdh_get_pubkey(sta->fils_ecdh, 1); + if (!pub) { + wpa_printf(MSG_DEBUG, + "FILS: Failed to derive ECDH public key"); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + elem_len = wpabuf_len(pub); + wpabuf_free(pub); + + /* Element */ + if ((size_t) (end - pos) < elem_len) { + wpa_printf(MSG_DEBUG, "FILS: No room for Element"); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + + wpabuf_clear_free(sta->fils_dh_ss); + sta->fils_dh_ss = crypto_ecdh_set_peerkey(sta->fils_ecdh, 1, + pos, elem_len); + if (!sta->fils_dh_ss) { + wpa_printf(MSG_DEBUG, "FILS: ECDH operation failed"); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + wpa_hexdump_buf_key(MSG_DEBUG, "FILS: DH_SS", sta->fils_dh_ss); + pos += elem_len; + } else { + crypto_ecdh_deinit(sta->fils_ecdh); + sta->fils_ecdh = NULL; + wpabuf_clear_free(sta->fils_dh_ss); + sta->fils_dh_ss = NULL; + } +#endif /* CONFIG_FILS_SK_PFS */ wpa_hexdump(MSG_DEBUG, "FILS: Remaining IEs", pos, end - pos); if (ieee802_11_parse_elems(pos, end - pos, &elems, 1) == ParseFailed) { @@ -1173,6 +1242,8 @@ static void handle_auth_fils_finish(struct hostapd_data *hapd, const u8 *pmk = NULL; size_t pmk_len = 0; u8 pmk_buf[PMK_LEN_MAX]; + struct wpabuf *pub = NULL; + u16 auth_alg; if (resp != WLAN_STATUS_SUCCESS) goto fail; @@ -1204,14 +1275,32 @@ static void handle_auth_fils_finish(struct hostapd_data *hapd, wpa_hexdump(MSG_DEBUG, "RSN: Generated FILS Nonce", fils_nonce, FILS_NONCE_LEN); - data = wpabuf_alloc(1000 + ielen); +#ifdef CONFIG_FILS_SK_PFS + if (sta->fils_dh_ss && sta->fils_ecdh) { + pub = crypto_ecdh_get_pubkey(sta->fils_ecdh, 1); + if (!pub) { + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + } +#endif /* CONFIG_FILS_SK_PFS */ + + data = wpabuf_alloc(1000 + ielen + (pub ? wpabuf_len(pub) : 0)); if (!data) { resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto fail; } - /* TODO: Finite Cyclic Group when using PK or PFS */ - /* TODO: Element when using PK or PFS */ + /* TODO: FILS PK */ +#ifdef CONFIG_FILS_SK_PFS + if (pub) { + /* Finite Cyclic Group */ + wpabuf_put_le16(data, hapd->conf->fils_dh_group); + + /* Element */ + wpabuf_put_buf(data, pub); + } +#endif /* CONFIG_FILS_SK_PFS */ /* RSNE */ wpabuf_put_data(data, ie, ielen); @@ -1243,7 +1332,11 @@ static void handle_auth_fils_finish(struct hostapd_data *hapd, if (fils_rmsk_to_pmk(wpa_auth_sta_key_mgmt(sta->wpa_sm), msk, msk_len, sta->fils_snonce, fils_nonce, - NULL, 0, pmk_buf, &pmk_len)) { + sta->fils_dh_ss ? + wpabuf_head(sta->fils_dh_ss) : NULL, + sta->fils_dh_ss ? + wpabuf_len(sta->fils_dh_ss) : 0, + pmk_buf, &pmk_len)) { wpa_printf(MSG_DEBUG, "FILS: Failed to derive PMK"); resp = WLAN_STATUS_UNSPECIFIED_FAILURE; wpabuf_free(data); @@ -1273,8 +1366,10 @@ static void handle_auth_fils_finish(struct hostapd_data *hapd, } fail: - send_auth_reply(hapd, sta->addr, hapd->own_addr, WLAN_AUTH_FILS_SK, 2, - resp, + auth_alg = (pub || + resp == WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED) ? + WLAN_AUTH_FILS_SK_PFS : WLAN_AUTH_FILS_SK; + send_auth_reply(hapd, sta->addr, hapd->own_addr, auth_alg, 2, resp, data ? wpabuf_head(data) : (u8 *) "", data ? wpabuf_len(data) : 0); wpabuf_free(data); @@ -1285,11 +1380,18 @@ fail: "authentication OK (FILS)"); sta->flags |= WLAN_STA_AUTH; wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH); - sta->auth_alg = WLAN_AUTH_FILS_SK; + sta->auth_alg = pub ? WLAN_AUTH_FILS_SK_PFS : WLAN_AUTH_FILS_SK; mlme_authenticate_indication(hapd, sta); } os_free(ie_buf); + wpabuf_free(pub); + wpabuf_clear_free(sta->fils_dh_ss); + sta->fils_dh_ss = NULL; +#ifdef CONFIG_FILS_SK_PFS + crypto_ecdh_deinit(sta->fils_ecdh); + sta->fils_ecdh = NULL; +#endif /* CONFIG_FILS_SK_PFS */ } @@ -1472,6 +1574,9 @@ static void handle_auth(struct hostapd_data *hapd, #ifdef CONFIG_FILS (hapd->conf->wpa && wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt) && auth_alg == WLAN_AUTH_FILS_SK) || + (hapd->conf->wpa && wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt) && + hapd->conf->fils_dh_group && + auth_alg == WLAN_AUTH_FILS_SK_PFS) || #endif /* CONFIG_FILS */ ((hapd->conf->auth_algs & WPA_AUTH_ALG_SHARED) && auth_alg == WLAN_AUTH_SHARED_KEY))) { @@ -1738,8 +1843,9 @@ static void handle_auth(struct hostapd_data *hapd, #endif /* CONFIG_SAE */ #ifdef CONFIG_FILS case WLAN_AUTH_FILS_SK: - handle_auth_fils(hapd, sta, mgmt, len, auth_transaction, - status_code); + case WLAN_AUTH_FILS_SK_PFS: + handle_auth_fils(hapd, sta, mgmt, len, auth_alg, + auth_transaction, status_code); return; #endif /* CONFIG_FILS */ } diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c index daacf7e44..4d5ec2f6b 100644 --- a/src/ap/ieee802_11_shared.c +++ b/src/ap/ieee802_11_shared.c @@ -630,7 +630,10 @@ u8 * hostapd_eid_fils_indic(struct hostapd_data *hapd, u8 *eid, int hessid) fils_info |= BIT(8); /* HESSID Included */ /* FILS Shared Key Authentication without PFS Supported */ fils_info |= BIT(9); - /* TODO: B10: FILS Shared Key Authentication with PFS Supported */ + if (hapd->conf->fils_dh_group) { + /* FILS Shared Key Authentication with PFS Supported */ + fils_info |= BIT(10); + } /* TODO: B11: FILS Public Key Authentication Supported */ /* B12..B15: Reserved */ WPA_PUT_LE16(pos, fils_info); diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c index f233d753a..ea217f387 100644 --- a/src/ap/sta_info.c +++ b/src/ap/sta_info.c @@ -345,6 +345,10 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) wpabuf_free(sta->fils_hlp_resp); wpabuf_free(sta->hlp_dhcp_discover); eloop_cancel_timeout(fils_hlp_timeout, hapd, sta); +#ifdef CONFIG_FILS_SK_PFS + crypto_ecdh_deinit(sta->fils_ecdh); + wpabuf_clear_free(sta->fils_dh_ss); +#endif /* CONFIG_FILS_SK_PFS */ #endif /* CONFIG_FILS */ #ifdef CONFIG_OWE diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h index b5d1d3315..029579841 100644 --- a/src/ap/sta_info.h +++ b/src/ap/sta_info.h @@ -231,6 +231,10 @@ struct sta_info { unsigned int fils_dhcp_rapid_commit_proxy:1; struct wpabuf *fils_hlp_resp; struct wpabuf *hlp_dhcp_discover; +#ifdef CONFIG_FILS_SK_PFS + struct crypto_ecdh *fils_ecdh; +#endif /* CONFIG_FILS_SK_PFS */ + struct wpabuf *fils_dh_ss; #endif /* CONFIG_FILS */ #ifdef CONFIG_OWE