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 ||