[07/25] OCV: Add configs for channel validation support

Message ID 20180806194643.1328-8-Mathy.Vanhoef@cs.kuleuven.be
State New
Headers show
Series
  • Add support for Operating Channel Validation (OCV)
Related show

Commit Message

Mathy Vanhoef Aug. 6, 2018, 7:46 p.m.
This commit adds compilation flags and configuration variables to
disable or enable Operating Channel Verification (OCV) support.

Signed-off-by: Mathy Vanhoef <Mathy.Vanhoef@cs.kuleuven.be>
---
 hostapd/Android.mk                        |  5 +++
 hostapd/Makefile                          |  5 +++
 hostapd/android.config                    |  3 ++
 hostapd/config_file.c                     |  6 ++++
 hostapd/defconfig                         |  3 ++
 hostapd/hostapd.conf                      |  7 ++++
 src/ap/ap_config.c                        |  9 ++++++
 src/ap/ap_config.h                        |  4 +++
 src/ap/hs20.c                             |  4 +++
 src/ap/wpa_auth.h                         |  5 +++
 src/ap/wpa_auth_glue.c                    |  3 ++
 src/ap/wpa_auth_i.h                       |  3 ++
 src/ap/wpa_auth_ie.c                      | 34 +++++++++++++++++++-
 src/common/wpa_common.h                   |  3 +-
 src/rsn_supp/wpa.c                        |  4 +++
 src/rsn_supp/wpa.h                        |  3 +-
 src/rsn_supp/wpa_ft.c                     |  2 ++
 src/rsn_supp/wpa_i.h                      |  1 +
 src/rsn_supp/wpa_ie.c                     |  2 ++
 tests/hwsim/example-hostapd.config        |  2 ++
 tests/hwsim/example-wpa_supplicant.config |  2 ++
 wlantest/Makefile                         |  1 +
 wlantest/bss.c                            |  5 +--
 wlantest/ctrl.c                           |  3 ++
 wlantest/sta.c                            | 11 +++++--
 wpa_supplicant/Android.mk                 |  5 +++
 wpa_supplicant/Makefile                   |  5 +++
 wpa_supplicant/android.config             |  3 ++
 wpa_supplicant/ap.c                       |  4 +++
 wpa_supplicant/config.c                   | 39 +++++++++++++++++++++++
 wpa_supplicant/config_file.c              |  9 ++++++
 wpa_supplicant/config_ssid.h              | 11 +++++++
 wpa_supplicant/defconfig                  |  5 ++-
 wpa_supplicant/mesh.c                     |  3 ++
 wpa_supplicant/mesh_rsn.c                 |  8 +++--
 wpa_supplicant/wpa_supplicant.c           |  3 ++
 wpa_supplicant/wpa_supplicant.conf        |  7 ++++
 37 files changed, 221 insertions(+), 11 deletions(-)

Patch

diff --git a/hostapd/Android.mk b/hostapd/Android.mk
index 322f6a632..82d43c754 100644
--- a/hostapd/Android.mk
+++ b/hostapd/Android.mk
@@ -235,6 +235,11 @@  L_CFLAGS += -DCONFIG_SUITEB192
 NEED_SHA384=y
 endif
 
+ifdef CONFIG_OCV
+L_CFLAGS += -DCONFIG_OCV
+CONFIG_IEEE80211W=y
+endif
+
 ifdef CONFIG_IEEE80211W
 L_CFLAGS += -DCONFIG_IEEE80211W
 NEED_SHA256=y
diff --git a/hostapd/Makefile b/hostapd/Makefile
index 2ce8b7ded..d88964c32 100644
--- a/hostapd/Makefile
+++ b/hostapd/Makefile
@@ -278,6 +278,11 @@  CFLAGS += -DCONFIG_SUITEB192
 NEED_SHA384=y
 endif
 
+ifdef CONFIG_OCV
+CFLAGS += -DCONFIG_OCV
+CONFIG_IEEE80211W=y
+endif
+
 ifdef CONFIG_IEEE80211W
 CFLAGS += -DCONFIG_IEEE80211W
 NEED_SHA256=y
diff --git a/hostapd/android.config b/hostapd/android.config
index 08d21f044..60734e166 100644
--- a/hostapd/android.config
+++ b/hostapd/android.config
@@ -50,6 +50,9 @@  CONFIG_DRIVER_NL80211_QCA=y
 # Driver support is also needed for IEEE 802.11w.
 CONFIG_IEEE80211W=y
 
+# Support Operating Channel Validation
+CONFIG_OCV=y
+
 # Integrated EAP server
 #CONFIG_EAP=y
 
diff --git a/hostapd/config_file.c b/hostapd/config_file.c
index 37308dbcc..03ba7a9bb 100644
--- a/hostapd/config_file.c
+++ b/hostapd/config_file.c
@@ -3296,6 +3296,12 @@  static int hostapd_config_fill(struct hostapd_config *conf,
 			return 1;
 		}
 #endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_OCV
+	} else if (os_strcmp(buf, "ocv") == 0) {
+		bss->ocv = atoi(pos);
+		if (bss->ocv && !bss->ieee80211w)
+			bss->ieee80211w = 1;
+#endif /* CONFIG_OCV */
 #ifdef CONFIG_IEEE80211N
 	} else if (os_strcmp(buf, "ieee80211n") == 0) {
 		conf->ieee80211n = atoi(pos);
diff --git a/hostapd/defconfig b/hostapd/defconfig
index c67c6622d..0a129aa34 100644
--- a/hostapd/defconfig
+++ b/hostapd/defconfig
@@ -53,6 +53,9 @@  CONFIG_RSN_PREAUTH=y
 # IEEE 802.11w (management frame protection)
 CONFIG_IEEE80211W=y
 
+# Support Operating Channel Validation
+CONFIG_OCV=y
+
 # Integrated EAP server
 CONFIG_EAP=y
 
diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
index 70f9713d3..e6e49f318 100644
--- a/hostapd/hostapd.conf
+++ b/hostapd/hostapd.conf
@@ -1418,6 +1418,13 @@  own_ip_addr=127.0.0.1
 # dot11AssociationSAQueryRetryTimeout, 1...4294967295
 #assoc_sa_query_retry_timeout=201
 
+# ocv: Operating Channel Validation
+# This is a countermeasure against multi-channel man-in-the-middle attacks.
+# Enabling this automatically also enables ieee80211w, if not yet enabled.
+# 0 = disabled (default)
+# 1 = enabled
+#ocv=1
+
 # disable_pmksa_caching: Disable PMKSA caching
 # This parameter can be used to disable caching of PMKSA created through EAP
 # authentication. RSN preauthentication may still end up using PMKSA caching if
diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
index 820cba956..27f014580 100644
--- a/src/ap/ap_config.c
+++ b/src/ap/ap_config.c
@@ -1002,6 +1002,15 @@  static int hostapd_config_check_bss(struct hostapd_bss_config *bss,
 	}
 #endif /* CONFIG_MBO */
 
+#ifdef CONFIG_OCV
+	if (full_config && bss->ieee80211w == NO_MGMT_FRAME_PROTECTION &&
+	    bss->ocv) {
+		wpa_printf(MSG_ERROR,
+			   "OCV: PMF needs to be enabled whenever using OCV");
+		return -1;
+	}
+#endif /* CONFIG_OCV */
+
 	return 0;
 }
 
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index 5b7112609..450295ada 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -42,6 +42,7 @@  struct mesh_conf {
 #define MESH_CONF_SEC_AMPE BIT(2)
 	unsigned int security;
 	enum mfp_options ieee80211w;
+	int ocv;
 	unsigned int pairwise_cipher;
 	unsigned int group_cipher;
 	unsigned int mgmt_group_cipher;
@@ -335,6 +336,9 @@  struct hostapd_bss_config {
 	/* dot11AssociationSAQueryRetryTimeout (in TUs) */
 	int assoc_sa_query_retry_timeout;
 #endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_OCV
+	int ocv; /* Operating Channel Validation */
+#endif /* CONFIG_OCV */
 	enum {
 		PSK_RADIUS_IGNORED = 0,
 		PSK_RADIUS_ACCEPTED = 1,
diff --git a/src/ap/hs20.c b/src/ap/hs20.c
index 98d016d96..4df844b5d 100644
--- a/src/ap/hs20.c
+++ b/src/ap/hs20.c
@@ -84,6 +84,10 @@  u8 * hostapd_eid_osen(struct hostapd_data *hapd, u8 *eid)
 			capab |= WPA_CAPABILITY_MFPR;
 	}
 #endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_OCV
+	if (hapd->conf->ocv)
+		capab |= WPA_CAPABILITY_OCVC;
+#endif /* CONFIG_OCV */
 	WPA_PUT_LE16(eid, capab);
 	eid += 2;
 
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
index 5837c3e9f..2762044ff 100644
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -192,6 +192,9 @@  struct wpa_auth_config {
 	int group_mgmt_cipher;
 	int sae_require_mfp;
 #endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_OCV
+	int ocv; /* Operating Channel Validation */
+#endif /* CONFIG_OCV */
 #ifdef CONFIG_IEEE80211R_AP
 	u8 ssid[SSID_MAX_LEN];
 	size_t ssid_len;
@@ -319,6 +322,8 @@  int wpa_validate_osen(struct wpa_authenticator *wpa_auth,
 		      struct wpa_state_machine *sm,
 		      const u8 *osen_ie, size_t osen_ie_len);
 int wpa_auth_uses_mfp(struct wpa_state_machine *sm);
+void wpa_auth_set_ocv(struct wpa_state_machine *sm, int ocv);
+int wpa_auth_uses_ocv(struct wpa_state_machine *sm);
 struct wpa_state_machine *
 wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr,
 		  const u8 *p2p_dev_addr);
diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
index 754b04462..1fbdc41a0 100644
--- a/src/ap/wpa_auth_glue.c
+++ b/src/ap/wpa_auth_glue.c
@@ -55,6 +55,9 @@  static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf,
 	wconf->wmm_enabled = conf->wmm_enabled;
 	wconf->wmm_uapsd = conf->wmm_uapsd;
 	wconf->disable_pmksa_caching = conf->disable_pmksa_caching;
+#ifdef CONFIG_OCV
+	wconf->ocv = conf->ocv;
+#endif /* CONFIG_OCV */
 	wconf->okc = conf->okc;
 #ifdef CONFIG_IEEE80211W
 	wconf->ieee80211w = conf->ieee80211w;
diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h
index b1cea1b49..a349304d5 100644
--- a/src/ap/wpa_auth_i.h
+++ b/src/ap/wpa_auth_i.h
@@ -92,6 +92,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;
+#endif /* CONFIG_OCV */
 
 	u8 req_replay_counter[WPA_REPLAY_COUNTER_LEN];
 	int req_replay_counter_used;
diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c
index 421dd5a6f..371be7bab 100644
--- a/src/ap/wpa_auth_ie.c
+++ b/src/ap/wpa_auth_ie.c
@@ -293,9 +293,13 @@  int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
 			capab |= WPA_CAPABILITY_MFPR;
 	}
 #endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_OCV
+	if (conf->ocv)
+		capab |= WPA_CAPABILITY_OCVC;
+#endif /* CONFIG_OCV */
 #ifdef CONFIG_RSN_TESTING
 	if (rsn_testing)
-		capab |= BIT(8) | BIT(14) | BIT(15);
+		capab |= BIT(8) | BIT(15);
 #endif /* CONFIG_RSN_TESTING */
 	WPA_PUT_LE16(pos, capab);
 	pos += 2;
@@ -414,6 +418,10 @@  static u8 * wpa_write_osen(struct wpa_auth_config *conf, u8 *eid)
 			capab |= WPA_CAPABILITY_MFPR;
 	}
 #endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_OCV
+	if (conf->ocv)
+		capab |= WPA_CAPABILITY_OCVC;
+#endif /* CONFIG_OCV */
 	WPA_PUT_LE16(eid, capab);
 	eid += 2;
 
@@ -759,6 +767,18 @@  int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
 	}
 #endif /* CONFIG_SAE */
 
+#ifdef CONFIG_OCV
+	if ((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;
+	}
+	if (wpa_auth->conf.ocv && (data.capabilities & WPA_CAPABILITY_OCVC))
+		wpa_auth_set_ocv(sm, 1);
+	else
+		wpa_auth_set_ocv(sm, 0);
+#endif /* CONFIG_OCV */
+
 	if (wpa_auth->conf.ieee80211w == NO_MGMT_FRAME_PROTECTION ||
 	    !(data.capabilities & WPA_CAPABILITY_MFPC))
 		sm->mgmt_frame_prot = 0;
@@ -1060,6 +1080,18 @@  int wpa_auth_uses_mfp(struct wpa_state_machine *sm)
 	return sm ? sm->mgmt_frame_prot : 0;
 }
 
+void wpa_auth_set_ocv(struct wpa_state_machine *sm, int ocv)
+{
+	if (sm == NULL)
+		return;
+	sm->ocv_enabled = ocv;
+}
+
+int wpa_auth_uses_ocv(struct wpa_state_machine *sm)
+{
+	return sm ? sm->ocv_enabled : 0;
+}
+
 
 #ifdef CONFIG_OWE
 u8 * wpa_auth_write_assoc_resp_owe(struct wpa_state_machine *sm,
diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h
index 626174440..7d12b5e52 100644
--- a/src/common/wpa_common.h
+++ b/src/common/wpa_common.h
@@ -148,7 +148,8 @@  WPA_CIPHER_BIP_CMAC_256)
 #define WPA_CAPABILITY_SPP_A_MSDU_REQUIRED BIT(11)
 #define WPA_CAPABILITY_PBAC BIT(12)
 #define WPA_CAPABILITY_EXT_KEY_ID_FOR_UNICAST BIT(13)
-/* B14-B15: Reserved */
+#define WPA_CAPABILITY_OCVC BIT(14)
+/* B15: Reserved */
 
 
 /* IEEE 802.11r */
diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c
index 56f3af799..774ddd9e4 100644
--- a/src/rsn_supp/wpa.c
+++ b/src/rsn_supp/wpa.c
@@ -2847,6 +2847,8 @@  int wpa_sm_set_param(struct wpa_sm *sm, enum wpa_sm_conf_params param,
 	case WPA_PARAM_MFP:
 		sm->mfp = value;
 		break;
+	case WPA_PARAM_OCV:
+		sm->ocv = value;
 	default:
 		break;
 	}
@@ -3800,6 +3802,8 @@  static int fils_ft_build_assoc_req_rsne(struct wpa_sm *sm, struct wpabuf *buf)
 	if (sm->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC)
 		capab |= WPA_CAPABILITY_MFPC;
 #endif /* CONFIG_IEEE80211W */
+	if (sm->ocv)
+		capab |= WPA_CAPABILITY_OCVC;
 	wpabuf_put_le16(buf, capab);
 
 	/* PMKID Count */
diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h
index d52b8e033..b832267a5 100644
--- a/src/rsn_supp/wpa.h
+++ b/src/rsn_supp/wpa.h
@@ -97,7 +97,8 @@  enum wpa_sm_conf_params {
 	WPA_PARAM_KEY_MGMT,
 	WPA_PARAM_MGMT_GROUP,
 	WPA_PARAM_RSN_ENABLED,
-	WPA_PARAM_MFP
+	WPA_PARAM_MFP,
+	WPA_PARAM_OCV
 };
 
 struct rsn_supp_config {
diff --git a/src/rsn_supp/wpa_ft.c b/src/rsn_supp/wpa_ft.c
index b8d60e320..9caff859d 100644
--- a/src/rsn_supp/wpa_ft.c
+++ b/src/rsn_supp/wpa_ft.c
@@ -242,6 +242,8 @@  static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len,
 	    sm->mgmt_group_cipher == WPA_CIPHER_BIP_CMAC_256)
 		capab |= WPA_CAPABILITY_MFPC;
 #endif /* CONFIG_IEEE80211W */
+	if (sm->ocv)
+		capab |= WPA_CAPABILITY_OCVC;
 	WPA_PUT_LE16(pos, capab);
 	pos += 2;
 
diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h
index d7ea29b81..8ef27bb31 100644
--- a/src/rsn_supp/wpa_i.h
+++ b/src/rsn_supp/wpa_i.h
@@ -86,6 +86,7 @@  struct wpa_sm {
 
 	int rsn_enabled; /* Whether RSN is enabled in configuration */
 	int mfp; /* 0 = disabled, 1 = optional, 2 = mandatory */
+	int ocv; /* Operating Channel Validation */
 
 	u8 *assoc_wpa_ie; /* Own WPA/RSN IE from (Re)AssocReq */
 	size_t assoc_wpa_ie_len;
diff --git a/src/rsn_supp/wpa_ie.c b/src/rsn_supp/wpa_ie.c
index a3410d154..ea2e92672 100644
--- a/src/rsn_supp/wpa_ie.c
+++ b/src/rsn_supp/wpa_ie.c
@@ -223,6 +223,8 @@  static int wpa_gen_wpa_ie_rsn(u8 *rsn_ie, size_t rsn_ie_len,
 	if (sm->mfp == 2)
 		capab |= WPA_CAPABILITY_MFPR;
 #endif /* CONFIG_IEEE80211W */
+	if (sm->ocv)
+		capab |= WPA_CAPABILITY_OCVC;
 	WPA_PUT_LE16(pos, capab);
 	pos += 2;
 
diff --git a/tests/hwsim/example-hostapd.config b/tests/hwsim/example-hostapd.config
index 71a207091..032465a9c 100644
--- a/tests/hwsim/example-hostapd.config
+++ b/tests/hwsim/example-hostapd.config
@@ -52,6 +52,8 @@  CONFIG_IEEE80211R=y
 CONFIG_IEEE80211N=y
 CONFIG_IEEE80211AC=y
 
+CONFIG_OCV=y
+
 CONFIG_WPS=y
 CONFIG_WPS_UPNP=y
 CONFIG_WPS_NFC=y
diff --git a/tests/hwsim/example-wpa_supplicant.config b/tests/hwsim/example-wpa_supplicant.config
index bc5dc2bbc..b681e9f55 100644
--- a/tests/hwsim/example-wpa_supplicant.config
+++ b/tests/hwsim/example-wpa_supplicant.config
@@ -63,6 +63,8 @@  CONFIG_IEEE80211R=y
 CONFIG_IEEE80211N=y
 CONFIG_IEEE80211AC=y
 
+CONFIG_OCV=y
+
 CONFIG_DEBUG_FILE=y
 
 CONFIG_WPS=y
diff --git a/wlantest/Makefile b/wlantest/Makefile
index 7104f4f58..e6c3123ac 100644
--- a/wlantest/Makefile
+++ b/wlantest/Makefile
@@ -47,6 +47,7 @@  OBJS_lib += ../src/crypto/libcrypto.a
 
 CFLAGS += -DCONFIG_PEERKEY
 CFLAGS += -DCONFIG_IEEE80211W
+CFLAGS += -DCONFIG_OCV
 CFLAGS += -DCONFIG_IEEE80211R
 CFLAGS += -DCONFIG_HS20
 CFLAGS += -DCONFIG_DEBUG_FILE
diff --git a/wlantest/bss.c b/wlantest/bss.c
index 04afe2b29..298a902c7 100644
--- a/wlantest/bss.c
+++ b/wlantest/bss.c
@@ -283,7 +283,7 @@  void bss_update(struct wlantest *wt, struct wlantest_bss *bss,
 		   "group=%s%s%s%s%s%s%s%s%s"
 		   "mgmt_group_cipher=%s%s%s%s%s"
 		   "key_mgmt=%s%s%s%s%s%s%s%s%s"
-		   "rsn_capab=%s%s%s%s%s",
+		   "rsn_capab=%s%s%s%s%s%s",
 		   MAC2STR(bss->bssid),
 		   bss->proto == 0 ? "OPEN " : "",
 		   bss->proto & WPA_PROTO_WPA ? "WPA " : "",
@@ -333,7 +333,8 @@  void bss_update(struct wlantest *wt, struct wlantest_bss *bss,
 		   bss->rsn_capab & WPA_CAPABILITY_MFPR ? "MFPR " : "",
 		   bss->rsn_capab & WPA_CAPABILITY_MFPC ? "MFPC " : "",
 		   bss->rsn_capab & WPA_CAPABILITY_PEERKEY_ENABLED ?
-		   "PEERKEY " : "");
+		   "PEERKEY " : "",
+		   bss->rsn_capab & WPA_CAPABILITY_OCVC ? "OCVC " : "");
 }
 
 
diff --git a/wlantest/ctrl.c b/wlantest/ctrl.c
index 7de0a8aff..587a0d3e1 100644
--- a/wlantest/ctrl.c
+++ b/wlantest/ctrl.c
@@ -982,6 +982,9 @@  static void info_print_rsn_capab(char *buf, size_t len, int capab)
 	if (capab & WPA_CAPABILITY_PEERKEY_ENABLED)
 		pos += os_snprintf(pos, end - pos, "%sPEERKEY",
 				   pos == buf ? "" : " ");
+	if (capab & WPA_CAPABILITY_OCVC)
+		pos += os_snprintf(pos, end - pos, "%sOCVC",
+				   pos == buf ? "" : " ");
 }
 
 
diff --git a/wlantest/sta.c b/wlantest/sta.c
index 1e53532a0..3e5ff51b6 100644
--- a/wlantest/sta.c
+++ b/wlantest/sta.c
@@ -168,13 +168,19 @@  void sta_update_assoc(struct wlantest_sta *sta, struct ieee802_11_elems *elems)
 			   "without MFP to BSS " MACSTR " that advertises "
 			   "MFPR", MAC2STR(sta->addr), MAC2STR(bss->bssid));
 	}
+	if ((sta->rsn_capab & WPA_CAPABILITY_OCVC) &&
+	    !(sta->rsn_capab & WPA_CAPABILITY_MFPC)) {
+		wpa_printf(MSG_INFO, "STA " MACSTR " tries to associate "
+			   "without MFP to BSS " MACSTR " while supporting "
+			   "OCV", MAC2STR(sta->addr), MAC2STR(bss->bssid));
+	}
 
 skip_rsn_wpa:
 	wpa_printf(MSG_INFO, "STA " MACSTR
 		   " proto=%s%s%s%s"
 		   "pairwise=%s%s%s%s%s%s%s"
 		   "key_mgmt=%s%s%s%s%s%s%s%s%s%s%s"
-		   "rsn_capab=%s%s%s%s%s",
+		   "rsn_capab=%s%s%s%s%s%s",
 		   MAC2STR(sta->addr),
 		   sta->proto == 0 ? "OPEN " : "",
 		   sta->proto & WPA_PROTO_WPA ? "WPA " : "",
@@ -210,5 +216,6 @@  skip_rsn_wpa:
 		   sta->rsn_capab & WPA_CAPABILITY_MFPR ? "MFPR " : "",
 		   sta->rsn_capab & WPA_CAPABILITY_MFPC ? "MFPC " : "",
 		   sta->rsn_capab & WPA_CAPABILITY_PEERKEY_ENABLED ?
-		   "PEERKEY " : "");
+		   "PEERKEY " : "",
+		   sta->rsn_capab & WPA_CAPABILITY_OCVC ? "OCVC " : "");
 }
diff --git a/wpa_supplicant/Android.mk b/wpa_supplicant/Android.mk
index a6809956d..f3b6d2cfa 100644
--- a/wpa_supplicant/Android.mk
+++ b/wpa_supplicant/Android.mk
@@ -207,6 +207,11 @@  L_CFLAGS += -DCONFIG_SUITEB192
 NEED_SHA384=y
 endif
 
+ifdef CONFIG_OCV
+L_CFLAGS += -DCONFIG_OCV
+CONFIG_IEEE80211W=y
+endif
+
 ifdef CONFIG_IEEE80211W
 L_CFLAGS += -DCONFIG_IEEE80211W
 NEED_SHA256=y
diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile
index c2e93e20b..69132bb57 100644
--- a/wpa_supplicant/Makefile
+++ b/wpa_supplicant/Makefile
@@ -240,6 +240,11 @@  CFLAGS += -DCONFIG_SUITEB192
 NEED_SHA384=y
 endif
 
+ifdef CONFIG_OCV
+CFLAGS += -DCONFIG_OCV
+CONFIG_IEEE80211W=y
+endif
+
 ifdef CONFIG_IEEE80211W
 CFLAGS += -DCONFIG_IEEE80211W
 NEED_SHA256=y
diff --git a/wpa_supplicant/android.config b/wpa_supplicant/android.config
index c97f59131..59a02ea3f 100644
--- a/wpa_supplicant/android.config
+++ b/wpa_supplicant/android.config
@@ -280,6 +280,9 @@  CONFIG_L2_PACKET=linux
 # Driver support is also needed for IEEE 802.11w.
 CONFIG_IEEE80211W=y
 
+# Support Operating Channel Validation
+CONFIG_OCV=y
+
 # Select TLS implementation
 # openssl = OpenSSL (default)
 # gnutls = GnuTLS
diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c
index ea846a0fa..7a25b3f6a 100644
--- a/wpa_supplicant/ap.c
+++ b/wpa_supplicant/ap.c
@@ -494,6 +494,10 @@  static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s,
 		bss->ieee80211w = ssid->ieee80211w;
 #endif /* CONFIG_IEEE80211W */
 
+#ifdef CONFIG_OCV
+	bss->ocv = ssid->ocv;
+#endif /* CONFIG_OCV */
+
 #ifdef CONFIG_WPS
 	/*
 	 * Enable WPS by default for open and WPA/WPA2-Personal network, but
diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
index dd7f6036c..655c0df0a 100644
--- a/wpa_supplicant/config.c
+++ b/wpa_supplicant/config.c
@@ -2035,6 +2035,42 @@  static char * wpa_config_write_mka_ckn(const struct parse_data *data,
 #endif /* CONFIG_MACSEC */
 
 
+#ifdef CONFIG_OCV
+
+static int wpa_config_parse_ocv(const struct parse_data *data,
+				    struct wpa_ssid *ssid, int line,
+				    const char *value)
+{
+	char *end;
+
+	ssid->ocv = strtol(value, &end, 0);
+	if (*end || ssid->ocv < 0 || ssid->ocv > 1) {
+		wpa_printf(MSG_ERROR, "Line %d: Invalid ocv value '%s'.",
+			   line, value);
+		return -1;
+	}
+	if (ssid->ocv && !ssid->ieee80211w)
+		ssid->ieee80211w = 1;
+	return 0;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_ocv(const struct parse_data *data,
+				       struct wpa_ssid *ssid)
+{
+	char *value = os_malloc(20);
+	if (value == NULL)
+		return NULL;
+	os_snprintf(value, 20, "%d", ssid->ocv);
+	value[20 - 1] = '\0';
+	return value;
+}
+#endif /* NO_CONFIG_WRITE */
+
+#endif /* CONFIG_OCV */
+
+
 static int wpa_config_parse_peerkey(const struct parse_data *data,
 				    struct wpa_ssid *ssid, int line,
 				    const char *value)
@@ -2238,6 +2274,9 @@  static const struct parse_data ssid_fields[] = {
 #ifdef CONFIG_IEEE80211W
 	{ INT_RANGE(ieee80211w, 0, 2) },
 #endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_OCV
+	{ FUNC(ocv) },
+#endif /* CONFIG_OCV */
 	{ FUNC(peerkey) /* obsolete - removed */ },
 	{ INT_RANGE(mixed_cell, 0, 1) },
 	{ INT_RANGE(frequency, 0, 65000) },
diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c
index aa73f9df6..cedec695e 100644
--- a/wpa_supplicant/config_file.c
+++ b/wpa_supplicant/config_file.c
@@ -160,6 +160,15 @@  static int wpa_config_validate_network(struct wpa_ssid *ssid, int line)
 		errors++;
 	}
 
+#ifdef CONFIG_OCV
+	if (ssid->ocv && ssid->ieee80211w == NO_MGMT_FRAME_PROTECTION) {
+		wpa_printf(MSG_ERROR,
+			   "Line %d: PMF needs to be enabled whenever using OCV",
+			   line);
+		errors++;
+	}
+#endif /* CONFIG_OCV */
+
 	return errors;
 }
 
diff --git a/wpa_supplicant/config_ssid.h b/wpa_supplicant/config_ssid.h
index 3000c43b4..e071820e4 100644
--- a/wpa_supplicant/config_ssid.h
+++ b/wpa_supplicant/config_ssid.h
@@ -457,6 +457,17 @@  struct wpa_ssid {
 	enum mfp_options ieee80211w;
 #endif /* CONFIG_IEEE80211W */
 
+#ifdef CONFIG_OCV
+	/**
+	 * ocv - Enable/disable operating channel validation
+	 *
+	 * If this parameter is set to 1, stations will exchange OCI element
+	 * to cryptographically verify the operating channel. Setting this
+	 * parameter to 0 disables this option. Default value: 0.
+	 */
+	int ocv;
+#endif /* CONFIG_OCV */
+
 	/**
 	 * frequency - Channel frequency in megahertz (MHz) for IBSS
 	 *
diff --git a/wpa_supplicant/defconfig b/wpa_supplicant/defconfig
index 08f585779..951e8b86c 100644
--- a/wpa_supplicant/defconfig
+++ b/wpa_supplicant/defconfig
@@ -299,7 +299,10 @@  CONFIG_BACKEND=file
 
 # IEEE 802.11w (management frame protection), also known as PMF
 # Driver support is also needed for IEEE 802.11w.
-#CONFIG_IEEE80211W=y
+CONFIG_IEEE80211W=y
+
+# Support Operating Channel Validation
+CONFIG_OCV=y
 
 # Select TLS implementation
 # openssl = OpenSSL (default)
diff --git a/wpa_supplicant/mesh.c b/wpa_supplicant/mesh.c
index 38b9fb320..3da8b1b24 100644
--- a/wpa_supplicant/mesh.c
+++ b/wpa_supplicant/mesh.c
@@ -93,6 +93,9 @@  static struct mesh_conf * mesh_config_create(struct wpa_supplicant *wpa_s,
 			conf->ieee80211w = NO_MGMT_FRAME_PROTECTION;
 	}
 #endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_OCV
+	conf->ocv = ssid->ocv;
+#endif /* CONFIG_OCV */
 
 	cipher = wpa_pick_pairwise_cipher(ssid->pairwise_cipher, 0);
 	if (cipher < 0 || cipher == WPA_CIPHER_TKIP) {
diff --git a/wpa_supplicant/mesh_rsn.c b/wpa_supplicant/mesh_rsn.c
index e74cb16b0..435c90b38 100644
--- a/wpa_supplicant/mesh_rsn.c
+++ b/wpa_supplicant/mesh_rsn.c
@@ -140,7 +140,7 @@  static int auth_start_ampe(void *ctx, const u8 *addr)
 
 
 static int __mesh_rsn_auth_init(struct mesh_rsn *rsn, const u8 *addr,
-				enum mfp_options ieee80211w)
+				enum mfp_options ieee80211w, int ocv)
 {
 	struct wpa_auth_config conf;
 	static const struct wpa_auth_callbacks cb = {
@@ -168,6 +168,9 @@  static int __mesh_rsn_auth_init(struct mesh_rsn *rsn, const u8 *addr,
 	if (ieee80211w != NO_MGMT_FRAME_PROTECTION)
 		conf.group_mgmt_cipher = rsn->mgmt_group_cipher;
 #endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_OCV
+	conf.ocv = ocv;
+#endif /* CONFIG_OCV */
 
 	rsn->auth = wpa_init(addr, &conf, &cb, rsn);
 	if (rsn->auth == NULL) {
@@ -219,7 +222,6 @@  static void mesh_rsn_deinit(struct mesh_rsn *rsn)
 		wpa_deinit(rsn->auth);
 }
 
-
 struct mesh_rsn *mesh_rsn_auth_init(struct wpa_supplicant *wpa_s,
 				    struct mesh_conf *conf)
 {
@@ -240,7 +242,7 @@  struct mesh_rsn *mesh_rsn_auth_init(struct wpa_supplicant *wpa_s,
 	mesh_rsn->mgmt_group_cipher = conf->mgmt_group_cipher;
 
 	if (__mesh_rsn_auth_init(mesh_rsn, wpa_s->own_addr,
-				 conf->ieee80211w) < 0) {
+				 conf->ieee80211w, conf->ocv) < 0) {
 		mesh_rsn_deinit(mesh_rsn);
 		os_free(mesh_rsn);
 		return NULL;
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index 185a8d50f..d761eadde 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -1505,6 +1505,9 @@  int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
 	wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_MFP,
 			 wpas_get_ssid_pmf(wpa_s, ssid));
 #endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_OCV
+	wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_OCV, ssid->ocv);
+#endif /* CONFIG_OCV */
 
 	if (wpa_sm_set_assoc_wpa_ie_default(wpa_s->wpa, wpa_ie, wpa_ie_len)) {
 		wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to generate WPA IE");
diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf
index 4f5916025..b93990004 100644
--- a/wpa_supplicant/wpa_supplicant.conf
+++ b/wpa_supplicant/wpa_supplicant.conf
@@ -907,6 +907,13 @@  fast_reauth=1
 # PMF required: ieee80211w=2 and key_mgmt=WPA-EAP-SHA256
 # (and similarly for WPA-PSK and WPA-WPSK-SHA256 if WPA2-Personal is used)
 #
+# ocv: whether operating channel validation is enabled
+# This is a countermeasure against multi-channel man-in-the-middle attacks.
+# Enabling this automatically also enables ieee80211w, if not yet enabled.
+# 0 = disabled (default)
+# 1 = enabled
+#ocv=1
+#
 # auth_alg: list of allowed IEEE 802.11 authentication algorithms
 # OPEN = Open System authentication (required for WPA/WPA2)
 # SHARED = Shared Key authentication (requires static WEP keys)