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 <vjakkam@codeaurora.org>
This commit is contained in:
Veerendranath Jakkam 2020-10-07 12:51:58 +05:30 committed by Jouni Malinen
parent d48a3a6768
commit debf3e2165
6 changed files with 60 additions and 15 deletions

View File

@ -1732,6 +1732,19 @@ own_ip_addr=127.0.0.1
# Enabling this automatically also enables ieee80211w, if not yet enabled. # Enabling this automatically also enables ieee80211w, if not yet enabled.
# 0 = disabled (default) # 0 = disabled (default)
# 1 = enabled # 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 #ocv=1
# disable_pmksa_caching: Disable PMKSA caching # disable_pmksa_caching: Disable PMKSA caching

View File

@ -3561,6 +3561,7 @@ static int check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
struct wpa_channel_info ci; struct wpa_channel_info ci;
int tx_chanwidth; int tx_chanwidth;
int tx_seg1_idx; int tx_seg1_idx;
enum oci_verify_result res;
if (hostapd_drv_channel_info(hapd, &ci) != 0) { if (hostapd_drv_channel_info(hapd, &ci) != 0) {
wpa_printf(MSG_WARNING, 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) &tx_seg1_idx) < 0)
return WLAN_STATUS_UNSPECIFIED_FAILURE; return WLAN_STATUS_UNSPECIFIED_FAILURE;
if (ocv_verify_tx_params(elems.oci, elems.oci_len, &ci, res = ocv_verify_tx_params(elems.oci, elems.oci_len, &ci,
tx_chanwidth, tx_seg1_idx) != tx_chanwidth, tx_seg1_idx);
OCI_SUCCESS) { 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", wpa_printf(MSG_WARNING, "FILS: OCV failed: %s",
ocv_errorstr); ocv_errorstr);
wpa_msg(hapd->msg_ctx, MSG_INFO, OCV_FAILURE "addr=" wpa_msg(hapd->msg_ctx, MSG_INFO, OCV_FAILURE "addr="

View File

@ -3037,6 +3037,7 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
struct wpa_channel_info ci; struct wpa_channel_info ci;
int tx_chanwidth; int tx_chanwidth;
int tx_seg1_idx; int tx_seg1_idx;
enum oci_verify_result res;
if (wpa_channel_info(wpa_auth, &ci) != 0) { if (wpa_channel_info(wpa_auth, &ci) != 0) {
wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
@ -3050,9 +3051,14 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
&tx_seg1_idx) < 0) &tx_seg1_idx) < 0)
return; return;
if (ocv_verify_tx_params(kde.oci, kde.oci_len, &ci, res = ocv_verify_tx_params(kde.oci, kde.oci_len, &ci,
tx_chanwidth, tx_seg1_idx) != tx_chanwidth, tx_seg1_idx);
OCI_SUCCESS) { 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, wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
"OCV failed: %s", ocv_errorstr); "OCV failed: %s", ocv_errorstr);
if (wpa_auth->conf.msg_ctx) if (wpa_auth->conf.msg_ctx)

View File

@ -3505,6 +3505,7 @@ int wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies,
struct wpa_channel_info ci; struct wpa_channel_info ci;
int tx_chanwidth; int tx_chanwidth;
int tx_seg1_idx; int tx_seg1_idx;
enum oci_verify_result res;
if (wpa_channel_info(sm->wpa_auth, &ci) != 0) { if (wpa_channel_info(sm->wpa_auth, &ci) != 0) {
wpa_printf(MSG_WARNING, 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) &tx_seg1_idx) < 0)
return WLAN_STATUS_UNSPECIFIED_FAILURE; return WLAN_STATUS_UNSPECIFIED_FAILURE;
if (ocv_verify_tx_params(parse.oci, parse.oci_len, &ci, res = ocv_verify_tx_params(parse.oci, parse.oci_len, &ci,
tx_chanwidth, tx_seg1_idx) != tx_chanwidth, tx_seg1_idx);
OCI_SUCCESS) { 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); wpa_printf(MSG_WARNING, "OCV failed: %s", ocv_errorstr);
if (sm->wpa_auth->conf.msg_ctx) if (sm->wpa_auth->conf.msg_ctx)
wpa_msg(sm->wpa_auth->conf.msg_ctx, MSG_INFO, wpa_msg(sm->wpa_auth->conf.msg_ctx, MSG_INFO,

View File

@ -95,8 +95,9 @@ struct wpa_state_machine {
#endif /* CONFIG_IEEE80211R_AP */ #endif /* CONFIG_IEEE80211R_AP */
unsigned int is_wnmsleep:1; unsigned int is_wnmsleep:1;
unsigned int pmkid_set:1; unsigned int pmkid_set:1;
#ifdef CONFIG_OCV #ifdef CONFIG_OCV
unsigned int ocv_enabled:1; int ocv_enabled;
#endif /* CONFIG_OCV */ #endif /* CONFIG_OCV */
u8 req_replay_counter[WPA_REPLAY_COUNTER_LEN]; u8 req_replay_counter[WPA_REPLAY_COUNTER_LEN];

View File

@ -810,12 +810,24 @@ wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
#ifdef CONFIG_OCV #ifdef CONFIG_OCV
if (wpa_auth->conf.ocv && (data.capabilities & WPA_CAPABILITY_OCVC) && if (wpa_auth->conf.ocv && (data.capabilities & WPA_CAPABILITY_OCVC) &&
!(data.capabilities & WPA_CAPABILITY_MFPC)) { !(data.capabilities & WPA_CAPABILITY_MFPC)) {
wpa_printf(MSG_DEBUG, /* Some legacy MFP incapable STAs wrongly copy OCVC bit from
"Management frame protection required with OCV, but client did not enable it"); * AP RSN capabilities. To improve interoperability with such
return WPA_MGMT_FRAME_PROTECTION_VIOLATION; * 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 */ #endif /* CONFIG_OCV */
if (wpa_auth->conf.ieee80211w == NO_MGMT_FRAME_PROTECTION || if (wpa_auth->conf.ieee80211w == NO_MGMT_FRAME_PROTECTION ||