From debf3e21658adf7da74f2b128dc611557bd6224f Mon Sep 17 00:00:00 2001 From: Veerendranath Jakkam Date: Wed, 7 Oct 2020 12:51:58 +0530 Subject: [PATCH] OCV: Work around for misbehaving STAs that indicate OCVC=1 without OCI Some legacy stations copy previously reserved RSN capability bits, including OCVC, in (Re)Association Request frames from the AP's RSNE but do not indicate MFP capability and/or do not send OCI in RSN handshakes. This is causing connection failures with such erroneous STAs. To improve interoperability with such legacy STAs allow a workaround OCV mode to be enabled to ignore OCVC=1 from the STA if it does not follow OCV requirements in the first protected exchange. This covers cases where a STA claims to have OCV capability, but it does not negotiate use of management frame protection or does not include OCI in EAPOL Key msg 2/4, FT Reassociation Request frame, or FILS (Re)Association Reqest. The previous behavior with ocv=1 is maintained, i.e., misbehaving STAs are not allowed to connect. When the new workaround mode is enabled with ocv=2, the AP considers STA as OCV capable on below criteria - STA indicates both OCV and MFP capability - STA sends OCI during connection attempt in a protected frame Enabling this workaround mode reduced OCV protection to some extend since it allows misbehavior to go through. As such, this should be enabled only if interoperability with misbehaving STAs is needed. Signed-off-by: Veerendranath Jakkam --- hostapd/hostapd.conf | 13 +++++++++++++ src/ap/ieee802_11.c | 13 ++++++++++--- src/ap/wpa_auth.c | 12 +++++++++--- src/ap/wpa_auth_ft.c | 12 +++++++++--- src/ap/wpa_auth_i.h | 3 ++- src/ap/wpa_auth_ie.c | 22 +++++++++++++++++----- 6 files changed, 60 insertions(+), 15 deletions(-) diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf index 25b4e4927..29717d2c6 100644 --- a/hostapd/hostapd.conf +++ b/hostapd/hostapd.conf @@ -1732,6 +1732,19 @@ own_ip_addr=127.0.0.1 # Enabling this automatically also enables ieee80211w, if not yet enabled. # 0 = disabled (default) # 1 = enabled +# 2 = enabled in workaround mode - Allow STA that claims OCV capability to +# connect even if the STA doesn't send OCI or negotiate PMF. This +# workaround is to improve interoperability with legacy STAs which are +# wrongly copying reserved bits of RSN capabilities from the AP's +# RSNE into (Re)Association Request frames. When this configuration is +# enabled, the AP considers STA is OCV capable only when the STA indicates +# MFP capability in (Re)Association Request frames and sends OCI in +# EAPOL-Key msg 2/4/FT Reassociation Request frame/FILS (Re)Association +# Request frame; otherwise, the AP disables OCV for the current connection +# with the STA. Enabling this workaround mode reduced OCV protection to +# some extend since it allows misbehavior to go through. As such, this +# should be enabled only if interoperability with misbehaving STAs is +# needed. #ocv=1 # disable_pmksa_caching: Disable PMKSA caching diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c index 78c1b8c8c..a1a27f102 100644 --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c @@ -3561,6 +3561,7 @@ static int check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, struct wpa_channel_info ci; int tx_chanwidth; int tx_seg1_idx; + enum oci_verify_result res; if (hostapd_drv_channel_info(hapd, &ci) != 0) { wpa_printf(MSG_WARNING, @@ -3574,9 +3575,15 @@ static int check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, &tx_seg1_idx) < 0) return WLAN_STATUS_UNSPECIFIED_FAILURE; - if (ocv_verify_tx_params(elems.oci, elems.oci_len, &ci, - tx_chanwidth, tx_seg1_idx) != - OCI_SUCCESS) { + res = ocv_verify_tx_params(elems.oci, elems.oci_len, &ci, + tx_chanwidth, tx_seg1_idx); + if (wpa_auth_uses_ocv(sta->wpa_sm) == 2 && + res == OCI_NOT_FOUND) { + /* Work around misbehaving STAs */ + wpa_printf(MSG_INFO, + "FILS: Disable OCV with a STA that does not send OCI"); + wpa_auth_set_ocv(sta->wpa_sm, 0); + } else if (res != OCI_SUCCESS) { wpa_printf(MSG_WARNING, "FILS: OCV failed: %s", ocv_errorstr); wpa_msg(hapd->msg_ctx, MSG_INFO, OCV_FAILURE "addr=" diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c index a5b1953f6..9d74bfcd7 100644 --- a/src/ap/wpa_auth.c +++ b/src/ap/wpa_auth.c @@ -3037,6 +3037,7 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) struct wpa_channel_info ci; int tx_chanwidth; int tx_seg1_idx; + enum oci_verify_result res; if (wpa_channel_info(wpa_auth, &ci) != 0) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, @@ -3050,9 +3051,14 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) &tx_seg1_idx) < 0) return; - if (ocv_verify_tx_params(kde.oci, kde.oci_len, &ci, - tx_chanwidth, tx_seg1_idx) != - OCI_SUCCESS) { + res = ocv_verify_tx_params(kde.oci, kde.oci_len, &ci, + tx_chanwidth, tx_seg1_idx); + if (wpa_auth_uses_ocv(sm) == 2 && res == OCI_NOT_FOUND) { + /* Work around misbehaving STAs */ + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, + "Disable OCV with a STA that does not send OCI"); + wpa_auth_set_ocv(sm, 0); + } else if (res != OCI_SUCCESS) { wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, "OCV failed: %s", ocv_errorstr); if (wpa_auth->conf.msg_ctx) diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c index 94a688ed9..5aa363eca 100644 --- a/src/ap/wpa_auth_ft.c +++ b/src/ap/wpa_auth_ft.c @@ -3505,6 +3505,7 @@ int wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies, struct wpa_channel_info ci; int tx_chanwidth; int tx_seg1_idx; + enum oci_verify_result res; if (wpa_channel_info(sm->wpa_auth, &ci) != 0) { wpa_printf(MSG_WARNING, @@ -3518,9 +3519,14 @@ int wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies, &tx_seg1_idx) < 0) return WLAN_STATUS_UNSPECIFIED_FAILURE; - if (ocv_verify_tx_params(parse.oci, parse.oci_len, &ci, - tx_chanwidth, tx_seg1_idx) != - OCI_SUCCESS) { + res = ocv_verify_tx_params(parse.oci, parse.oci_len, &ci, + tx_chanwidth, tx_seg1_idx); + if (wpa_auth_uses_ocv(sm) == 2 && res == OCI_NOT_FOUND) { + /* Work around misbehaving STAs */ + wpa_printf(MSG_INFO, + "Disable OCV with a STA that does not send OCI"); + wpa_auth_set_ocv(sm, 0); + } else if (res != OCI_SUCCESS) { wpa_printf(MSG_WARNING, "OCV failed: %s", ocv_errorstr); if (sm->wpa_auth->conf.msg_ctx) wpa_msg(sm->wpa_auth->conf.msg_ctx, MSG_INFO, diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h index ba08ac257..a6dc1a591 100644 --- a/src/ap/wpa_auth_i.h +++ b/src/ap/wpa_auth_i.h @@ -95,8 +95,9 @@ struct wpa_state_machine { #endif /* CONFIG_IEEE80211R_AP */ unsigned int is_wnmsleep:1; unsigned int pmkid_set:1; + #ifdef CONFIG_OCV - unsigned int ocv_enabled:1; + int ocv_enabled; #endif /* CONFIG_OCV */ u8 req_replay_counter[WPA_REPLAY_COUNTER_LEN]; diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c index 3a16997cf..3704fc05e 100644 --- a/src/ap/wpa_auth_ie.c +++ b/src/ap/wpa_auth_ie.c @@ -810,12 +810,24 @@ wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, #ifdef CONFIG_OCV if (wpa_auth->conf.ocv && (data.capabilities & WPA_CAPABILITY_OCVC) && !(data.capabilities & WPA_CAPABILITY_MFPC)) { - wpa_printf(MSG_DEBUG, - "Management frame protection required with OCV, but client did not enable it"); - return WPA_MGMT_FRAME_PROTECTION_VIOLATION; + /* Some legacy MFP incapable STAs wrongly copy OCVC bit from + * AP RSN capabilities. To improve interoperability with such + * legacy STAs allow connection without enabling OCV when the + * workaround mode (ocv=2) is enabled. + */ + if (wpa_auth->conf.ocv == 2) { + wpa_printf(MSG_DEBUG, + "Allow connecting MFP incapable and OCV capable STA without enabling OCV"); + wpa_auth_set_ocv(sm, 0); + } else { + wpa_printf(MSG_DEBUG, + "Management frame protection required with OCV, but client did not enable it"); + return WPA_MGMT_FRAME_PROTECTION_VIOLATION; + } + } else { + wpa_auth_set_ocv(sm, (data.capabilities & WPA_CAPABILITY_OCVC) ? + wpa_auth->conf.ocv : 0); } - wpa_auth_set_ocv(sm, wpa_auth->conf.ocv && - (data.capabilities & WPA_CAPABILITY_OCVC)); #endif /* CONFIG_OCV */ if (wpa_auth->conf.ieee80211w == NO_MGMT_FRAME_PROTECTION ||