Cumulative patch from commit 06f14421ea8644d12a7b0be6b583114869f9c451 (DO NOT MERGE)

06f1442 TLS: Parse OCSPResponse to extract BasicOCSPResponse
d560288 TLS: Parse CertificateStatus message
eeba168 TLS: Add status_request ClientHello extension if OCSP is requested
4303d53 TLS: Parse ServerHello extensions
6b7bb42 TLS: Add minimal support for PKCS #12
5ce2941 TLS: Extend PKCS #5 to support PKCS #12 style key decryption
f6a62df TLS: Fix and complete ASN.1 tag list
3c108b7 EAP peer: External server certificate chain validation
b6e5e14 EAP-FAST peer: Fix PAC parser error messages
5b904b3 EAP-FAST: Check T-PRF result in MSK/EMSK derivation
b1d8c5c EAP-FAST peer: Fix error path handling for Session-Id
36478a1 OpenSSL: Support new API for HMAC/EVP_MD_CTX in OpenSSL 1.1.x-pre1
9257610 FT: Fix FTIE generation for EAPOL-Key msg 3/4
e44bd28 FT: Fix sm->assoc_resp_ftie storing on the AP side
59e78c2 FT: Fix FTIE generation for 4-way handshake after FT protocol run
b0ecbd3 AP: Use more readable version of management group cipher in error cases
651c6a8 Add TEST_ASSOC_IE for WPA/RSN IE testing on AP side
58059e6 FST: Print debug entry on MB IE update based on EVENT_AUTH
af041f9 dbus: Add support for vendor specific elements
5c8acf7 EAP-IKEv2: Check HMAC SHA1/MD5 result
7b991b4 Use proper build config for parsing proxy_arp
4db29e6 TLS: Add support for PKCS #5 v2.0 PBES2

Change-Id: I10b71e4d3573ef60a52ea6ff56afcd3a06a0b7b0
Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>
diff --git a/wpa_supplicant/Android.mk b/wpa_supplicant/Android.mk
index e748785..f62b30a 100644
--- a/wpa_supplicant/Android.mk
+++ b/wpa_supplicant/Android.mk
@@ -980,6 +980,7 @@
 OBJS += src/tls/tlsv1_client.c
 OBJS += src/tls/tlsv1_client_write.c
 OBJS += src/tls/tlsv1_client_read.c
+OBJS += src/tls/tlsv1_client_ocsp.c
 OBJS += src/tls/asn1.c
 OBJS += src/tls/rsa.c
 OBJS += src/tls/x509v3.c
diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile
index e3d3acf..6bab7d1 100644
--- a/wpa_supplicant/Makefile
+++ b/wpa_supplicant/Makefile
@@ -1010,6 +1010,7 @@
 OBJS += ../src/tls/tlsv1_client.o
 OBJS += ../src/tls/tlsv1_client_write.o
 OBJS += ../src/tls/tlsv1_client_read.o
+OBJS += ../src/tls/tlsv1_client_ocsp.o
 OBJS += ../src/tls/asn1.o
 OBJS += ../src/tls/rsa.o
 OBJS += ../src/tls/x509v3.o
diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
index 7b45a3a..a3b587f 100644
--- a/wpa_supplicant/ctrl_iface.c
+++ b/wpa_supplicant/ctrl_iface.c
@@ -6952,6 +6952,7 @@
 	wpa_s->extra_roc_dur = 0;
 	wpa_s->test_failure = WPAS_TEST_FAILURE_NONE;
 	wpa_s->p2p_go_csa_on_inv = 0;
+	wpa_sm_set_test_assoc_ie(wpa_s->wpa, NULL);
 #endif /* CONFIG_TESTING_OPTIONS */
 
 	wpa_s->disconnected = 0;
@@ -7841,62 +7842,36 @@
 				      (void *) (intptr_t) count);
 }
 
-#endif /* CONFIG_TESTING_OPTIONS */
 
-
-static void wpas_ctrl_vendor_elem_update(struct wpa_supplicant *wpa_s)
+static int wpas_ctrl_test_assoc_ie(struct wpa_supplicant *wpa_s,
+				   const char *cmd)
 {
-	unsigned int i;
-	char buf[30];
+	struct wpabuf *buf;
+	size_t len;
 
-	wpa_printf(MSG_DEBUG, "Update vendor elements");
+	len = os_strlen(cmd);
+	if (len & 1)
+		return -1;
+	len /= 2;
 
-	for (i = 0; i < NUM_VENDOR_ELEM_FRAMES; i++) {
-		if (wpa_s->vendor_elem[i]) {
-			int res;
+	if (len == 0) {
+		buf = NULL;
+	} else {
+		buf = wpabuf_alloc(len);
+		if (buf == NULL)
+			return -1;
 
-			res = os_snprintf(buf, sizeof(buf), "frame[%u]", i);
-			if (!os_snprintf_error(sizeof(buf), res)) {
-				wpa_hexdump_buf(MSG_DEBUG, buf,
-						wpa_s->vendor_elem[i]);
-			}
+		if (hexstr2bin(cmd, wpabuf_put(buf, len), len) < 0) {
+			wpabuf_free(buf);
+			return -1;
 		}
 	}
 
-#ifdef CONFIG_P2P
-	if (wpa_s->parent == wpa_s &&
-	    wpa_s->global->p2p &&
-	    !wpa_s->global->p2p_disabled)
-		p2p_set_vendor_elems(wpa_s->global->p2p, wpa_s->vendor_elem);
-#endif /* CONFIG_P2P */
+	wpa_sm_set_test_assoc_ie(wpa_s->wpa, buf);
+	return 0;
 }
 
-
-static struct wpa_supplicant *
-wpas_ctrl_vendor_elem_iface(struct wpa_supplicant *wpa_s,
-			    enum wpa_vendor_elem_frame frame)
-{
-	switch (frame) {
-#ifdef CONFIG_P2P
-	case VENDOR_ELEM_PROBE_REQ_P2P:
-	case VENDOR_ELEM_PROBE_RESP_P2P:
-	case VENDOR_ELEM_PROBE_RESP_P2P_GO:
-	case VENDOR_ELEM_BEACON_P2P_GO:
-	case VENDOR_ELEM_P2P_PD_REQ:
-	case VENDOR_ELEM_P2P_PD_RESP:
-	case VENDOR_ELEM_P2P_GO_NEG_REQ:
-	case VENDOR_ELEM_P2P_GO_NEG_RESP:
-	case VENDOR_ELEM_P2P_GO_NEG_CONF:
-	case VENDOR_ELEM_P2P_INV_REQ:
-	case VENDOR_ELEM_P2P_INV_RESP:
-	case VENDOR_ELEM_P2P_ASSOC_REQ:
-	case VENDOR_ELEM_P2P_ASSOC_RESP:
-		return wpa_s->parent;
-#endif /* CONFIG_P2P */
-	default:
-		return wpa_s;
-	}
-}
+#endif /* CONFIG_TESTING_OPTIONS */
 
 
 static int wpas_ctrl_vendor_elem_add(struct wpa_supplicant *wpa_s, char *cmd)
@@ -7910,7 +7885,7 @@
 	frame = atoi(pos);
 	if (frame < 0 || frame >= NUM_VENDOR_ELEM_FRAMES)
 		return -1;
-	wpa_s = wpas_ctrl_vendor_elem_iface(wpa_s, frame);
+	wpa_s = wpas_vendor_elem(wpa_s, frame);
 
 	pos = os_strchr(pos, ' ');
 	if (pos == NULL)
@@ -7941,7 +7916,7 @@
 
 	if (wpa_s->vendor_elem[frame] == NULL) {
 		wpa_s->vendor_elem[frame] = buf;
-		wpas_ctrl_vendor_elem_update(wpa_s);
+		wpas_vendor_elem_update(wpa_s);
 		return 0;
 	}
 
@@ -7952,7 +7927,7 @@
 
 	wpabuf_put_buf(wpa_s->vendor_elem[frame], buf);
 	wpabuf_free(buf);
-	wpas_ctrl_vendor_elem_update(wpa_s);
+	wpas_vendor_elem_update(wpa_s);
 
 	return 0;
 }
@@ -7965,7 +7940,7 @@
 
 	if (frame < 0 || frame >= NUM_VENDOR_ELEM_FRAMES)
 		return -1;
-	wpa_s = wpas_ctrl_vendor_elem_iface(wpa_s, frame);
+	wpa_s = wpas_vendor_elem(wpa_s, frame);
 
 	if (wpa_s->vendor_elem[frame] == NULL)
 		return 0;
@@ -7983,12 +7958,12 @@
 	size_t len;
 	u8 *buf;
 	struct ieee802_11_elems elems;
-	u8 *ie, *end;
+	int res;
 
 	frame = atoi(pos);
 	if (frame < 0 || frame >= NUM_VENDOR_ELEM_FRAMES)
 		return -1;
-	wpa_s = wpas_ctrl_vendor_elem_iface(wpa_s, frame);
+	wpa_s = wpas_vendor_elem(wpa_s, frame);
 
 	pos = os_strchr(pos, ' ');
 	if (pos == NULL)
@@ -7998,7 +7973,7 @@
 	if (*pos == '*') {
 		wpabuf_free(wpa_s->vendor_elem[frame]);
 		wpa_s->vendor_elem[frame] = NULL;
-		wpas_ctrl_vendor_elem_update(wpa_s);
+		wpas_vendor_elem_update(wpa_s);
 		return 0;
 	}
 
@@ -8026,31 +8001,9 @@
 		return -1;
 	}
 
-	ie = wpabuf_mhead_u8(wpa_s->vendor_elem[frame]);
-	end = ie + wpabuf_len(wpa_s->vendor_elem[frame]);
-
-	for (; ie + 1 < end; ie += 2 + ie[1]) {
-		if (ie + len > end)
-			break;
-		if (os_memcmp(ie, buf, len) != 0)
-			continue;
-
-		if (wpabuf_len(wpa_s->vendor_elem[frame]) == len) {
-			wpabuf_free(wpa_s->vendor_elem[frame]);
-			wpa_s->vendor_elem[frame] = NULL;
-		} else {
-			os_memmove(ie, ie + len,
-				   end - (ie + len));
-			wpa_s->vendor_elem[frame]->used -= len;
-		}
-		os_free(buf);
-		wpas_ctrl_vendor_elem_update(wpa_s);
-		return 0;
-	}
-
+	res = wpas_vendor_elem_remove(wpa_s, frame, buf, len);
 	os_free(buf);
-
-	return -1;
+	return res;
 }
 
 
@@ -8882,6 +8835,9 @@
 	} else if (os_strncmp(buf, "EVENT_TEST ", 11) == 0) {
 		if (wpas_ctrl_event_test(wpa_s, buf + 11) < 0)
 			reply_len = -1;
+	} else if (os_strncmp(buf, "TEST_ASSOC_IE ", 14) == 0) {
+		if (wpas_ctrl_test_assoc_ie(wpa_s, buf + 14) < 0)
+			reply_len = -1;
 #endif /* CONFIG_TESTING_OPTIONS */
 	} else if (os_strncmp(buf, "VENDOR_ELEM_ADD ", 16) == 0) {
 		if (wpas_ctrl_vendor_elem_add(wpa_s, buf + 16) < 0)
diff --git a/wpa_supplicant/dbus/dbus_new.c b/wpa_supplicant/dbus/dbus_new.c
index 440b8cf..91a1511 100644
--- a/wpa_supplicant/dbus/dbus_new.c
+++ b/wpa_supplicant/dbus/dbus_new.c
@@ -3070,6 +3070,30 @@
 	  }
 	},
 #endif /* CONFIG_TDLS */
+	{ "VendorElemAdd", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_vendor_elem_add,
+	  {
+		  { "frame_id", "i", ARG_IN },
+		  { "ielems", "ay", ARG_IN },
+		  END_ARGS
+	  }
+	},
+	{ "VendorElemGet", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_vendor_elem_get,
+	  {
+		  { "frame_id", "i", ARG_IN },
+		  { "ielems", "ay", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+	{ "VendorElemRem", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_vendor_elem_remove,
+	  {
+		  { "frame_id", "i", ARG_IN },
+		  { "ielems", "ay", ARG_IN },
+		  END_ARGS
+	  }
+	},
 #ifndef CONFIG_NO_CONFIG_WRITE
 	{ "SaveConfig", WPAS_DBUS_NEW_IFACE_INTERFACE,
 	  (WPADBusMethodHandler) wpas_dbus_handler_save_config,
diff --git a/wpa_supplicant/dbus/dbus_new_handlers.c b/wpa_supplicant/dbus/dbus_new_handlers.c
index fbfcdc9..6b77008 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers.c
+++ b/wpa_supplicant/dbus/dbus_new_handlers.c
@@ -4363,3 +4363,147 @@
 }
 
 #endif /* CONFIG_AP */
+
+
+DBusMessage * wpas_dbus_handler_vendor_elem_add(DBusMessage *message,
+						struct wpa_supplicant *wpa_s)
+{
+	u8 *ielems;
+	int len;
+	struct ieee802_11_elems elems;
+	dbus_int32_t frame_id;
+	DBusMessageIter	iter, array;
+
+	dbus_message_iter_init(message, &iter);
+	dbus_message_iter_get_basic(&iter, &frame_id);
+	if (frame_id < 0 || frame_id >= NUM_VENDOR_ELEM_FRAMES) {
+		return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
+					      "Invalid ID");
+	}
+
+	dbus_message_iter_next(&iter);
+	dbus_message_iter_recurse(&iter, &array);
+	dbus_message_iter_get_fixed_array(&array, &ielems, &len);
+	if (!ielems || len == 0) {
+		return dbus_message_new_error(
+			message, DBUS_ERROR_INVALID_ARGS, "Invalid value");
+	}
+
+	if (ieee802_11_parse_elems(ielems, len, &elems, 0) == ParseFailed) {
+		return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
+					      "Parse error");
+	}
+
+	wpa_s = wpas_vendor_elem(wpa_s, frame_id);
+	if (!wpa_s->vendor_elem[frame_id]) {
+		wpa_s->vendor_elem[frame_id] = wpabuf_alloc_copy(ielems, len);
+		wpas_vendor_elem_update(wpa_s);
+		return NULL;
+	}
+
+	if (wpabuf_resize(&wpa_s->vendor_elem[frame_id], len) < 0) {
+		return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
+					      "Resize error");
+	}
+
+	wpabuf_put_data(wpa_s->vendor_elem[frame_id], ielems, len);
+	wpas_vendor_elem_update(wpa_s);
+	return NULL;
+}
+
+
+DBusMessage * wpas_dbus_handler_vendor_elem_get(DBusMessage *message,
+						struct wpa_supplicant *wpa_s)
+{
+	DBusMessage *reply;
+	DBusMessageIter	iter, array_iter;
+	dbus_int32_t frame_id;
+	const u8 *elem;
+	size_t elem_len;
+
+	dbus_message_iter_init(message, &iter);
+	dbus_message_iter_get_basic(&iter, &frame_id);
+
+	if (frame_id < 0 || frame_id >= NUM_VENDOR_ELEM_FRAMES) {
+		return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
+					      "Invalid ID");
+	}
+
+	wpa_s = wpas_vendor_elem(wpa_s, frame_id);
+	if (!wpa_s->vendor_elem[frame_id]) {
+		return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
+					      "ID value does not exist");
+	}
+
+	reply = dbus_message_new_method_return(message);
+	if (!reply)
+		return wpas_dbus_error_no_memory(message);
+
+	dbus_message_iter_init_append(reply, &iter);
+
+	elem = wpabuf_head_u8(wpa_s->vendor_elem[frame_id]);
+	elem_len = wpabuf_len(wpa_s->vendor_elem[frame_id]);
+
+	if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+					      DBUS_TYPE_BYTE_AS_STRING,
+					      &array_iter) ||
+	    !dbus_message_iter_append_fixed_array(&array_iter, DBUS_TYPE_BYTE,
+						  &elem, elem_len) ||
+	    !dbus_message_iter_close_container(&iter, &array_iter)) {
+		dbus_message_unref(reply);
+		reply = wpas_dbus_error_no_memory(message);
+	}
+
+	return reply;
+}
+
+
+DBusMessage * wpas_dbus_handler_vendor_elem_remove(DBusMessage *message,
+						   struct wpa_supplicant *wpa_s)
+{
+	u8 *ielems;
+	int len;
+	struct ieee802_11_elems elems;
+	DBusMessageIter	iter, array;
+	dbus_int32_t frame_id;
+
+	dbus_message_iter_init(message, &iter);
+	dbus_message_iter_get_basic(&iter, &frame_id);
+	if (frame_id < 0 || frame_id >= NUM_VENDOR_ELEM_FRAMES) {
+		return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
+					      "Invalid ID");
+	}
+
+	dbus_message_iter_next(&iter);
+	dbus_message_iter_recurse(&iter, &array);
+	dbus_message_iter_get_fixed_array(&array, &ielems, &len);
+	if (!ielems || len == 0) {
+		return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
+					      "Invalid value");
+	}
+
+	wpa_s = wpas_vendor_elem(wpa_s, frame_id);
+
+	if (len == 1 && *ielems == '*') {
+		wpabuf_free(wpa_s->vendor_elem[frame_id]);
+		wpa_s->vendor_elem[frame_id] = NULL;
+		wpas_vendor_elem_update(wpa_s);
+		return NULL;
+	}
+
+	if (!wpa_s->vendor_elem[frame_id]) {
+		return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
+					      "ID value does not exist");
+	}
+
+	if (ieee802_11_parse_elems(ielems, len, &elems, 0) == ParseFailed) {
+		return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
+					      "Parse error");
+	}
+
+	if (wpas_vendor_elem_remove(wpa_s, frame_id, ielems, len) == 0)
+		return NULL;
+
+	return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
+				      "Not found");
+}
diff --git a/wpa_supplicant/dbus/dbus_new_handlers.h b/wpa_supplicant/dbus/dbus_new_handlers.h
index 86c8359..31b92d1 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers.h
+++ b/wpa_supplicant/dbus/dbus_new_handlers.h
@@ -194,6 +194,13 @@
 DBusMessage * wpas_dbus_handler_tdls_teardown(DBusMessage *message,
 					      struct wpa_supplicant *wpa_s);
 
+DBusMessage * wpas_dbus_handler_vendor_elem_add(DBusMessage *message,
+						struct wpa_supplicant *wpa_s);
+DBusMessage * wpas_dbus_handler_vendor_elem_get(DBusMessage *message,
+						struct wpa_supplicant *wpa_s);
+DBusMessage * wpas_dbus_handler_vendor_elem_remove(
+	DBusMessage *message, struct wpa_supplicant *wpa_s);
+
 DBusMessage * wpas_dbus_handler_save_config(DBusMessage *message,
 					    struct wpa_supplicant *wpa_s);
 
diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
index 6eb3e88..2675bed 100644
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -3329,7 +3329,10 @@
 	switch (event) {
 	case EVENT_AUTH:
 #ifdef CONFIG_FST
-		wpas_fst_update_mbie(wpa_s, data->auth.ies, data->auth.ies_len);
+		if (!wpas_fst_update_mbie(wpa_s, data->auth.ies,
+					  data->auth.ies_len))
+			wpa_printf(MSG_DEBUG,
+				   "FST: MB IEs updated from auth IE");
 #endif /* CONFIG_FST */
 		sme_event_auth(wpa_s, data);
 		break;
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index 7e5c07a..6e2d180 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -5556,6 +5556,16 @@
 		if (wpa_s->wpa_state == WPA_SCANNING && !wpa_s->scanning)
 			wpa_supplicant_req_scan(wpa_s, 0, 0);
 		break;
+	case WPA_CTRL_REQ_EXT_CERT_CHECK:
+		if (eap->pending_ext_cert_check != PENDING_CHECK)
+			return -1;
+		if (os_strcmp(value, "good") == 0)
+			eap->pending_ext_cert_check = EXT_CERT_CHECK_GOOD;
+		else if (os_strcmp(value, "bad") == 0)
+			eap->pending_ext_cert_check = EXT_CERT_CHECK_BAD;
+		else
+			return -1;
+		break;
 	default:
 		wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown field '%s'", field);
 		return -1;
@@ -6143,3 +6153,86 @@
 	}
 	wpabuf_free(buf);
 }
+
+
+struct wpa_supplicant *
+wpas_vendor_elem(struct wpa_supplicant *wpa_s, enum wpa_vendor_elem_frame frame)
+{
+	switch (frame) {
+#ifdef CONFIG_P2P
+	case VENDOR_ELEM_PROBE_REQ_P2P:
+	case VENDOR_ELEM_PROBE_RESP_P2P:
+	case VENDOR_ELEM_PROBE_RESP_P2P_GO:
+	case VENDOR_ELEM_BEACON_P2P_GO:
+	case VENDOR_ELEM_P2P_PD_REQ:
+	case VENDOR_ELEM_P2P_PD_RESP:
+	case VENDOR_ELEM_P2P_GO_NEG_REQ:
+	case VENDOR_ELEM_P2P_GO_NEG_RESP:
+	case VENDOR_ELEM_P2P_GO_NEG_CONF:
+	case VENDOR_ELEM_P2P_INV_REQ:
+	case VENDOR_ELEM_P2P_INV_RESP:
+	case VENDOR_ELEM_P2P_ASSOC_REQ:
+	case VENDOR_ELEM_P2P_ASSOC_RESP:
+		return wpa_s->parent;
+#endif /* CONFIG_P2P */
+	default:
+		return wpa_s;
+	}
+}
+
+
+void wpas_vendor_elem_update(struct wpa_supplicant *wpa_s)
+{
+	unsigned int i;
+	char buf[30];
+
+	wpa_printf(MSG_DEBUG, "Update vendor elements");
+
+	for (i = 0; i < NUM_VENDOR_ELEM_FRAMES; i++) {
+		if (wpa_s->vendor_elem[i]) {
+			int res;
+
+			res = os_snprintf(buf, sizeof(buf), "frame[%u]", i);
+			if (!os_snprintf_error(sizeof(buf), res)) {
+				wpa_hexdump_buf(MSG_DEBUG, buf,
+						wpa_s->vendor_elem[i]);
+			}
+		}
+	}
+
+#ifdef CONFIG_P2P
+	if (wpa_s->parent == wpa_s &&
+	    wpa_s->global->p2p &&
+	    !wpa_s->global->p2p_disabled)
+		p2p_set_vendor_elems(wpa_s->global->p2p, wpa_s->vendor_elem);
+#endif /* CONFIG_P2P */
+}
+
+
+int wpas_vendor_elem_remove(struct wpa_supplicant *wpa_s, int frame,
+			    const u8 *elem, size_t len)
+{
+	u8 *ie, *end;
+
+	ie = wpabuf_mhead_u8(wpa_s->vendor_elem[frame]);
+	end = ie + wpabuf_len(wpa_s->vendor_elem[frame]);
+
+	for (; ie + 1 < end; ie += 2 + ie[1]) {
+		if (ie + len > end)
+			break;
+		if (os_memcmp(ie, elem, len) != 0)
+			continue;
+
+		if (wpabuf_len(wpa_s->vendor_elem[frame]) == len) {
+			wpabuf_free(wpa_s->vendor_elem[frame]);
+			wpa_s->vendor_elem[frame] = NULL;
+		} else {
+			os_memmove(ie, ie + len, end - (ie + len));
+			wpa_s->vendor_elem[frame]->used -= len;
+		}
+		wpas_vendor_elem_update(wpa_s);
+		return 0;
+	}
+
+	return -1;
+}
diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf
index 2ce1cc4..e33b720 100644
--- a/wpa_supplicant/wpa_supplicant.conf
+++ b/wpa_supplicant/wpa_supplicant.conf
@@ -1037,6 +1037,12 @@
 #	that have issues interoperating with updated TLS version)
 # tls_disable_tlsv1_2=1 - disable use of TLSv1.2 (a workaround for AAA servers
 #	that have issues interoperating with updated TLS version)
+# tls_ext_cert_check=0 - No external server certificate validation (default)
+# tls_ext_cert_check=1 - External server certificate validation enabled; this
+#	requires an external program doing validation of server certificate
+#	chain when receiving CTRL-RSP-EXT_CERT_CHECK event from the control
+#	interface and report the result of the validation with
+#	CTRL-RSP_EXT_CERT_CHECK.
 #
 # Following certificate/private key fields are used in inner Phase2
 # authentication when using EAP-TTLS or EAP-PEAP.
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index a8b273b..05a1bc6 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -1179,6 +1179,12 @@
 
 void wpas_network_reenabled(void *eloop_ctx, void *timeout_ctx);
 
+void wpas_vendor_elem_update(struct wpa_supplicant *wpa_s);
+struct wpa_supplicant * wpas_vendor_elem(struct wpa_supplicant *wpa_s,
+					 enum wpa_vendor_elem_frame frame);
+int wpas_vendor_elem_remove(struct wpa_supplicant *wpa_s, int frame,
+			    const u8 *elem, size_t len);
+
 #ifdef CONFIG_FST
 
 struct fst_wpa_obj;
diff --git a/wpa_supplicant/wpas_glue.c b/wpa_supplicant/wpas_glue.c
index 7986695..f84c8b9 100644
--- a/wpa_supplicant/wpas_glue.c
+++ b/wpa_supplicant/wpas_glue.c
@@ -739,6 +739,8 @@
 		return WPA_CTRL_REQ_SIM;
 	else if (os_strcmp(field, "PSK_PASSPHRASE") == 0)
 		return WPA_CTRL_REQ_PSK_PASSPHRASE;
+	else if (os_strcmp(field, "EXT_CERT_CHECK") == 0)
+		return WPA_CTRL_REQ_EXT_CERT_CHECK;
 	return WPA_CTRL_REQ_UNKNOWN;
 }
 
@@ -782,6 +784,10 @@
 		*txt = "PSK or passphrase";
 		ret = "PSK_PASSPHRASE";
 		break;
+	case WPA_CTRL_REQ_EXT_CERT_CHECK:
+		*txt = "External server certificate validation";
+		ret = "EXT_CERT_CHECK";
+		break;
 	default:
 		break;
 	}
@@ -837,6 +843,8 @@
 	if (ssid == NULL)
 		return;
 
+	if (field == WPA_CTRL_REQ_EXT_CERT_CHECK)
+		ssid->eap.pending_ext_cert_check = PENDING_CHECK;
 	wpas_notify_network_request(wpa_s, ssid, field, default_txt);
 
 	field_name = wpa_supplicant_ctrl_req_to_string(field, default_txt,