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 <j@w1.fi>
This commit is contained in:
parent
cad291d671
commit
1764559eef
10 changed files with 149 additions and 14 deletions
|
@ -272,6 +272,10 @@ L_CFLAGS += -DCONFIG_FILS
|
||||||
OBJS += src/ap/fils_hlp.c
|
OBJS += src/ap/fils_hlp.c
|
||||||
NEED_SHA384=y
|
NEED_SHA384=y
|
||||||
NEED_AES_SIV=y
|
NEED_AES_SIV=y
|
||||||
|
ifdef CONFIG_FILS_SK_PFS
|
||||||
|
L_CFLAGS += -DCONFIG_FILS_SK_PFS
|
||||||
|
NEED_ECC=y
|
||||||
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifdef CONFIG_WNM
|
ifdef CONFIG_WNM
|
||||||
|
|
|
@ -316,6 +316,10 @@ CFLAGS += -DCONFIG_FILS
|
||||||
OBJS += ../src/ap/fils_hlp.o
|
OBJS += ../src/ap/fils_hlp.o
|
||||||
NEED_SHA384=y
|
NEED_SHA384=y
|
||||||
NEED_AES_SIV=y
|
NEED_AES_SIV=y
|
||||||
|
ifdef CONFIG_FILS_SK_PFS
|
||||||
|
CFLAGS += -DCONFIG_FILS_SK_PFS
|
||||||
|
NEED_ECC=y
|
||||||
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifdef CONFIG_WNM
|
ifdef CONFIG_WNM
|
||||||
|
|
|
@ -3658,6 +3658,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
|
||||||
} else if (os_strcmp(buf, "fils_realm") == 0) {
|
} else if (os_strcmp(buf, "fils_realm") == 0) {
|
||||||
if (parse_fils_realm(bss, pos) < 0)
|
if (parse_fils_realm(bss, pos) < 0)
|
||||||
return 1;
|
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) {
|
} else if (os_strcmp(buf, "dhcp_server") == 0) {
|
||||||
if (hostapd_parse_ip_addr(pos, &bss->dhcp_server)) {
|
if (hostapd_parse_ip_addr(pos, &bss->dhcp_server)) {
|
||||||
wpa_printf(MSG_ERROR,
|
wpa_printf(MSG_ERROR,
|
||||||
|
|
|
@ -358,6 +358,8 @@ CONFIG_IPV6=y
|
||||||
# Note: This is an experimental and not yet complete implementation. This
|
# Note: This is an experimental and not yet complete implementation. This
|
||||||
# should not be enabled for production use.
|
# should not be enabled for production use.
|
||||||
#CONFIG_FILS=y
|
#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
|
# Include internal line edit mode in hostapd_cli. This can be used to provide
|
||||||
# limited command line editing and history support.
|
# limited command line editing and history support.
|
||||||
|
|
|
@ -1393,6 +1393,11 @@ own_ip_addr=127.0.0.1
|
||||||
#fils_realm=example.com
|
#fils_realm=example.com
|
||||||
#fils_realm=example.org
|
#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
|
# DHCP server for FILS HLP
|
||||||
# If configured, hostapd will act as a DHCP relay for all FILS HLP requests
|
# 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
|
# that include a DHCPDISCOVER message and send them to the specific DHCP
|
||||||
|
|
|
@ -610,6 +610,7 @@ struct hostapd_bss_config {
|
||||||
u8 fils_cache_id[FILS_CACHE_ID_LEN];
|
u8 fils_cache_id[FILS_CACHE_ID_LEN];
|
||||||
int fils_cache_id_set;
|
int fils_cache_id_set;
|
||||||
struct dl_list fils_realms; /* list of struct fils_realm */
|
struct dl_list fils_realms; /* list of struct fils_realm */
|
||||||
|
int fils_dh_group;
|
||||||
struct hostapd_ip_addr dhcp_server;
|
struct hostapd_ip_addr dhcp_server;
|
||||||
int dhcp_rapid_commit_proxy;
|
int dhcp_rapid_commit_proxy;
|
||||||
unsigned int fils_hlp_wait_time;
|
unsigned int fils_hlp_wait_time;
|
||||||
|
|
|
@ -1014,8 +1014,9 @@ static void handle_auth_fils_finish(struct hostapd_data *hapd,
|
||||||
const u8 *msk, size_t msk_len);
|
const u8 *msk, size_t msk_len);
|
||||||
|
|
||||||
static void handle_auth_fils(struct hostapd_data *hapd, struct sta_info *sta,
|
static void handle_auth_fils(struct hostapd_data *hapd, struct sta_info *sta,
|
||||||
const struct ieee80211_mgmt *mgmt, size_t len,
|
const struct ieee80211_mgmt *mgmt, size_t len,
|
||||||
u16 auth_transaction, u16 status_code)
|
u16 auth_alg, u16 auth_transaction,
|
||||||
|
u16 status_code)
|
||||||
{
|
{
|
||||||
u16 resp = WLAN_STATUS_SUCCESS;
|
u16 resp = WLAN_STATUS_SUCCESS;
|
||||||
const u8 *pos, *end;
|
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",
|
wpa_hexdump(MSG_DEBUG, "FILS: Authentication frame fields",
|
||||||
pos, end - pos);
|
pos, end - pos);
|
||||||
|
|
||||||
/* TODO: Finite Cyclic Group when using PK or PFS */
|
/* TODO: FILS PK */
|
||||||
/* TODO: Element when using PK or PFS */
|
#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);
|
wpa_hexdump(MSG_DEBUG, "FILS: Remaining IEs", pos, end - pos);
|
||||||
if (ieee802_11_parse_elems(pos, end - pos, &elems, 1) == ParseFailed) {
|
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;
|
const u8 *pmk = NULL;
|
||||||
size_t pmk_len = 0;
|
size_t pmk_len = 0;
|
||||||
u8 pmk_buf[PMK_LEN_MAX];
|
u8 pmk_buf[PMK_LEN_MAX];
|
||||||
|
struct wpabuf *pub = NULL;
|
||||||
|
u16 auth_alg;
|
||||||
|
|
||||||
if (resp != WLAN_STATUS_SUCCESS)
|
if (resp != WLAN_STATUS_SUCCESS)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
@ -1204,14 +1275,32 @@ static void handle_auth_fils_finish(struct hostapd_data *hapd,
|
||||||
wpa_hexdump(MSG_DEBUG, "RSN: Generated FILS Nonce",
|
wpa_hexdump(MSG_DEBUG, "RSN: Generated FILS Nonce",
|
||||||
fils_nonce, FILS_NONCE_LEN);
|
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) {
|
if (!data) {
|
||||||
resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
|
resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO: Finite Cyclic Group when using PK or PFS */
|
/* TODO: FILS PK */
|
||||||
/* TODO: Element when using PK or PFS */
|
#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 */
|
/* RSNE */
|
||||||
wpabuf_put_data(data, ie, ielen);
|
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),
|
if (fils_rmsk_to_pmk(wpa_auth_sta_key_mgmt(sta->wpa_sm),
|
||||||
msk, msk_len, sta->fils_snonce, fils_nonce,
|
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");
|
wpa_printf(MSG_DEBUG, "FILS: Failed to derive PMK");
|
||||||
resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
|
resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
|
||||||
wpabuf_free(data);
|
wpabuf_free(data);
|
||||||
|
@ -1273,8 +1366,10 @@ static void handle_auth_fils_finish(struct hostapd_data *hapd,
|
||||||
}
|
}
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
send_auth_reply(hapd, sta->addr, hapd->own_addr, WLAN_AUTH_FILS_SK, 2,
|
auth_alg = (pub ||
|
||||||
resp,
|
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_head(data) : (u8 *) "",
|
||||||
data ? wpabuf_len(data) : 0);
|
data ? wpabuf_len(data) : 0);
|
||||||
wpabuf_free(data);
|
wpabuf_free(data);
|
||||||
|
@ -1285,11 +1380,18 @@ fail:
|
||||||
"authentication OK (FILS)");
|
"authentication OK (FILS)");
|
||||||
sta->flags |= WLAN_STA_AUTH;
|
sta->flags |= WLAN_STA_AUTH;
|
||||||
wpa_auth_sm_event(sta->wpa_sm, WPA_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);
|
mlme_authenticate_indication(hapd, sta);
|
||||||
}
|
}
|
||||||
|
|
||||||
os_free(ie_buf);
|
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
|
#ifdef CONFIG_FILS
|
||||||
(hapd->conf->wpa && wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt) &&
|
(hapd->conf->wpa && wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt) &&
|
||||||
auth_alg == WLAN_AUTH_FILS_SK) ||
|
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 */
|
#endif /* CONFIG_FILS */
|
||||||
((hapd->conf->auth_algs & WPA_AUTH_ALG_SHARED) &&
|
((hapd->conf->auth_algs & WPA_AUTH_ALG_SHARED) &&
|
||||||
auth_alg == WLAN_AUTH_SHARED_KEY))) {
|
auth_alg == WLAN_AUTH_SHARED_KEY))) {
|
||||||
|
@ -1738,8 +1843,9 @@ static void handle_auth(struct hostapd_data *hapd,
|
||||||
#endif /* CONFIG_SAE */
|
#endif /* CONFIG_SAE */
|
||||||
#ifdef CONFIG_FILS
|
#ifdef CONFIG_FILS
|
||||||
case WLAN_AUTH_FILS_SK:
|
case WLAN_AUTH_FILS_SK:
|
||||||
handle_auth_fils(hapd, sta, mgmt, len, auth_transaction,
|
case WLAN_AUTH_FILS_SK_PFS:
|
||||||
status_code);
|
handle_auth_fils(hapd, sta, mgmt, len, auth_alg,
|
||||||
|
auth_transaction, status_code);
|
||||||
return;
|
return;
|
||||||
#endif /* CONFIG_FILS */
|
#endif /* CONFIG_FILS */
|
||||||
}
|
}
|
||||||
|
|
|
@ -630,7 +630,10 @@ u8 * hostapd_eid_fils_indic(struct hostapd_data *hapd, u8 *eid, int hessid)
|
||||||
fils_info |= BIT(8); /* HESSID Included */
|
fils_info |= BIT(8); /* HESSID Included */
|
||||||
/* FILS Shared Key Authentication without PFS Supported */
|
/* FILS Shared Key Authentication without PFS Supported */
|
||||||
fils_info |= BIT(9);
|
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 */
|
/* TODO: B11: FILS Public Key Authentication Supported */
|
||||||
/* B12..B15: Reserved */
|
/* B12..B15: Reserved */
|
||||||
WPA_PUT_LE16(pos, fils_info);
|
WPA_PUT_LE16(pos, fils_info);
|
||||||
|
|
|
@ -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->fils_hlp_resp);
|
||||||
wpabuf_free(sta->hlp_dhcp_discover);
|
wpabuf_free(sta->hlp_dhcp_discover);
|
||||||
eloop_cancel_timeout(fils_hlp_timeout, hapd, sta);
|
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 */
|
#endif /* CONFIG_FILS */
|
||||||
|
|
||||||
#ifdef CONFIG_OWE
|
#ifdef CONFIG_OWE
|
||||||
|
|
|
@ -231,6 +231,10 @@ struct sta_info {
|
||||||
unsigned int fils_dhcp_rapid_commit_proxy:1;
|
unsigned int fils_dhcp_rapid_commit_proxy:1;
|
||||||
struct wpabuf *fils_hlp_resp;
|
struct wpabuf *fils_hlp_resp;
|
||||||
struct wpabuf *hlp_dhcp_discover;
|
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 */
|
#endif /* CONFIG_FILS */
|
||||||
|
|
||||||
#ifdef CONFIG_OWE
|
#ifdef CONFIG_OWE
|
||||||
|
|
Loading…
Reference in a new issue