Cumulative patch from commit 324ade51e168f28430f4429849becd0f08d507c0 (DO NOT MERGE)

324ade5 TLS: Make tls_cert_chain_failure_event() more robust
c6231b5 TLS: Remove storing of never-read value
15a6813 Remove unnecessary cleanup assignment in SHA1Final()
ef3866a nl80211: Don't call linux_iface_up() for a dedicated P2P Device
c2ed779 mesh: Document Mesh Peering Management element structure in more detail
b2817cd mesh: Check PMKID in AMPE Action frames
6c33eed mesh: Fix PMKID to match the standard
ede7770 wpa_supplicant: Do not wait for monitor on P2P Device interface
1c94570 Do not wait for monitor to attach if no control interface
f98674a Clone default LIBS value to LIBS_* for other tools
7d2f674 Add "GET_CAPABILITY acs" to allow ACS build option to be detected
d990971 wpa_supplicant: Enable Automatic Channel Selection support for AP mode
96bc508 Handle survey event properly in wpa_supplicant
d39f796 EAP-TNC peer: Remove dead code related to fragmentation
662512e P2PS: Remove dead code
abbbaa4 TNC: Print received IF-TNCCS message as debug ASCII hexdump
d745f02 EAP-TNC peer: Allow fragment_size to be configured
a67e7e5 RADIUS: Add EACCES to list of recognized send() errno values
5bd9be4 Fix RADIUS Called-Station-Id to not escape SSID
0764dd6 TLS client: Multi-OCSP check to cover intermediate CAs
d6b536f Add ocsp=3 configuration parameter for multi-OCSP
0268383 TLS: Move variable declaration to the beginning of the block
b567775 TLS client: OCSP stapling with ocsp_multi option (RFC 6961)
8ea6a27 TLS server: OCSP stapling with ocsp_multi option (RFC 6961)
5addb0d Server configuration for OCSP stapling with ocsp_multi (RFC 6961)
bca0872 TLS server: OCSP stapling
9532bd2 GnuTLS: OCSP stapling on the server side
6241766 Use wpa_msg() for the "RSN: PMKID mismatch" message
e161451 EAP-EKE: Merge identical error return paths
13cb0a6 EAP-EKE: Reject too long Prot() data when building a frame
0ab0de8 Document previously missing key_mgmt values

Change-Id: I9ac7d0da03d8baf4542e276ab20cb56e44bfa33c
Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>
diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
index cf9b2ce..88074f2 100644
--- a/src/ap/ap_config.c
+++ b/src/ap/ap_config.c
@@ -471,6 +471,7 @@
 	os_free(conf->private_key);
 	os_free(conf->private_key_passwd);
 	os_free(conf->ocsp_stapling_response);
+	os_free(conf->ocsp_stapling_response_multi);
 	os_free(conf->dh_file);
 	os_free(conf->openssl_ciphers);
 	os_free(conf->pac_opaque_encr_key);
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index ff9dcb0..44bcccc 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -341,6 +341,7 @@
 	int check_crl;
 	unsigned int tls_session_lifetime;
 	char *ocsp_stapling_response;
+	char *ocsp_stapling_response_multi;
 	char *dh_file;
 	char *openssl_ciphers;
 	u8 *pac_opaque_encr_key;
diff --git a/src/ap/authsrv.c b/src/ap/authsrv.c
index c9111f6..cdb49cd 100644
--- a/src/ap/authsrv.c
+++ b/src/ap/authsrv.c
@@ -173,6 +173,8 @@
 		params.openssl_ciphers = hapd->conf->openssl_ciphers;
 		params.ocsp_stapling_response =
 			hapd->conf->ocsp_stapling_response;
+		params.ocsp_stapling_response_multi =
+			hapd->conf->ocsp_stapling_response_multi;
 
 		if (tls_global_set_params(hapd->ssl_ctx, &params)) {
 			wpa_printf(MSG_ERROR, "Failed to set TLS parameters");
diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
index fd07201..aad0d81 100644
--- a/src/ap/drv_callbacks.c
+++ b/src/ap/drv_callbacks.c
@@ -549,8 +549,8 @@
 
 
 #ifdef CONFIG_ACS
-static void hostapd_acs_channel_selected(struct hostapd_data *hapd,
-					 struct acs_selected_channels *acs_res)
+void hostapd_acs_channel_selected(struct hostapd_data *hapd,
+				  struct acs_selected_channels *acs_res)
 {
 	int ret, i;
 
@@ -945,6 +945,8 @@
 	ieee802_1x_receive(hapd, src, data, data_len);
 }
 
+#endif /* HOSTAPD */
+
 
 static struct hostapd_channel_data * hostapd_get_mode_channel(
 	struct hostapd_iface *iface, unsigned int freq)
@@ -1019,10 +1021,9 @@
 }
 
 
-static void hostapd_event_get_survey(struct hostapd_data *hapd,
-				     struct survey_results *survey_results)
+void hostapd_event_get_survey(struct hostapd_iface *iface,
+			      struct survey_results *survey_results)
 {
-	struct hostapd_iface *iface = hapd->iface;
 	struct freq_survey *survey, *tmp;
 	struct hostapd_channel_data *chan;
 
@@ -1054,6 +1055,7 @@
 }
 
 
+#ifdef HOSTAPD
 #ifdef NEED_AP_MLME
 
 static void hostapd_event_iface_unavailable(struct hostapd_data *hapd)
@@ -1261,7 +1263,7 @@
 			data->connect_failed_reason.code);
 		break;
 	case EVENT_SURVEY:
-		hostapd_event_get_survey(hapd, &data->survey_results);
+		hostapd_event_get_survey(hapd->iface, &data->survey_results);
 		break;
 #ifdef NEED_AP_MLME
 	case EVENT_INTERFACE_UNAVAILABLE:
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index 8161a59..7b59f80 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -493,6 +493,11 @@
 			 int ssi_signal);
 void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht,
 			     int offset, int width, int cf1, int cf2);
+struct survey_results;
+void hostapd_event_get_survey(struct hostapd_iface *iface,
+			      struct survey_results *survey_results);
+void hostapd_acs_channel_selected(struct hostapd_data *hapd,
+				  struct acs_selected_channels *acs_res);
 
 const struct hostapd_eap_user *
 hostapd_get_eap_user(struct hostapd_data *hapd, const u8 *identity,
diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c
index f566603..e3b3d94 100644
--- a/src/ap/ieee802_1x.c
+++ b/src/ap/ieee802_1x.c
@@ -475,6 +475,7 @@
 {
 	char buf[128];
 	struct hostapd_radius_attr *attr;
+	int len;
 
 	if (!hostapd_config_get_radius_attr(req_attr,
 					    RADIUS_ATTR_NAS_IP_ADDRESS) &&
@@ -506,15 +507,15 @@
 		return -1;
 	}
 
-	os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":%s",
-		    MAC2STR(hapd->own_addr),
-		    wpa_ssid_txt(hapd->conf->ssid.ssid,
-				 hapd->conf->ssid.ssid_len));
-	buf[sizeof(buf) - 1] = '\0';
+	len = os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":",
+			  MAC2STR(hapd->own_addr));
+	os_memcpy(&buf[len], hapd->conf->ssid.ssid,
+		  hapd->conf->ssid.ssid_len);
+	len += hapd->conf->ssid.ssid_len;
 	if (!hostapd_config_get_radius_attr(req_attr,
 					    RADIUS_ATTR_CALLED_STATION_ID) &&
 	    !radius_msg_add_attr(msg, RADIUS_ATTR_CALLED_STATION_ID,
-				 (u8 *) buf, os_strlen(buf))) {
+				 (u8 *) buf, len)) {
 		wpa_printf(MSG_ERROR, "Could not add Called-Station-Id");
 		return -1;
 	}
diff --git a/src/common/sae.c b/src/common/sae.c
index b962ea2..6c00a7e 100644
--- a/src/common/sae.c
+++ b/src/common/sae.c
@@ -816,6 +816,7 @@
 	os_memset(keyseed, 0, sizeof(keyseed));
 	os_memcpy(sae->tmp->kck, keys, SAE_KCK_LEN);
 	os_memcpy(sae->pmk, keys + SAE_KCK_LEN, SAE_PMK_LEN);
+	os_memcpy(sae->pmkid, val, SAE_PMKID_LEN);
 	os_memset(keys, 0, sizeof(keys));
 	wpa_hexdump_key(MSG_DEBUG, "SAE: KCK", sae->tmp->kck, SAE_KCK_LEN);
 	wpa_hexdump_key(MSG_DEBUG, "SAE: PMK", sae->pmk, SAE_PMK_LEN);
diff --git a/src/common/sae.h b/src/common/sae.h
index c07026c..a4270bc 100644
--- a/src/common/sae.h
+++ b/src/common/sae.h
@@ -45,6 +45,7 @@
 	enum { SAE_NOTHING, SAE_COMMITTED, SAE_CONFIRMED, SAE_ACCEPTED } state;
 	u16 send_confirm;
 	u8 pmk[SAE_PMK_LEN];
+	u8 pmkid[SAE_PMKID_LEN];
 	struct crypto_bignum *peer_commit_scalar;
 	int group;
 	int sync;
diff --git a/src/crypto/sha1-internal.c b/src/crypto/sha1-internal.c
index f6658e6..ffcba66 100644
--- a/src/crypto/sha1-internal.c
+++ b/src/crypto/sha1-internal.c
@@ -297,7 +297,6 @@
 			 255);
 	}
 	/* Wipe variables */
-	i = 0;
 	os_memset(context->buffer, 0, 64);
 	os_memset(context->state, 0, 20);
 	os_memset(context->count, 0, 8);
diff --git a/src/crypto/tls.h b/src/crypto/tls.h
index bca94d6..453b4de 100644
--- a/src/crypto/tls.h
+++ b/src/crypto/tls.h
@@ -96,6 +96,7 @@
 #define TLS_CONN_EAP_FAST BIT(7)
 #define TLS_CONN_DISABLE_TLSv1_0 BIT(8)
 #define TLS_CONN_EXT_CERT_CHECK BIT(9)
+#define TLS_CONN_REQUIRE_OCSP_ALL BIT(10)
 
 /**
  * struct tls_connection_params - Parameters for TLS connection
@@ -140,6 +141,9 @@
  * @flags: Parameter options (TLS_CONN_*)
  * @ocsp_stapling_response: DER encoded file with cached OCSP stapling response
  *	or %NULL if OCSP is not enabled
+ * @ocsp_stapling_response_multi: DER encoded file with cached OCSP stapling
+ *	response list (OCSPResponseList for ocsp_multi in RFC 6961) or %NULL if
+ *	ocsp_multi is not enabled
  *
  * TLS connection parameters to be configured with tls_connection_set_params()
  * and tls_global_set_params().
@@ -180,6 +184,7 @@
 
 	unsigned int flags;
 	const char *ocsp_stapling_response;
+	const char *ocsp_stapling_response_multi;
 };
 
 
diff --git a/src/crypto/tls_gnutls.c b/src/crypto/tls_gnutls.c
index b1bec4a..c4cd3c1 100644
--- a/src/crypto/tls_gnutls.c
+++ b/src/crypto/tls_gnutls.c
@@ -37,6 +37,8 @@
 			 union tls_event_data *data);
 	void *cb_ctx;
 	int cert_in_cb;
+
+	char *ocsp_stapling_response;
 };
 
 struct tls_connection {
@@ -133,6 +135,7 @@
 		if (global->params_set)
 			gnutls_certificate_free_credentials(global->xcred);
 		os_free(global->session_data);
+		os_free(global->ocsp_stapling_response);
 		os_free(global);
 	}
 
@@ -347,6 +350,12 @@
 	if (conn == NULL || params == NULL)
 		return -1;
 
+	if (params->flags & TLS_CONN_REQUIRE_OCSP_ALL) {
+		wpa_printf(MSG_INFO,
+			   "GnuTLS: ocsp=3 not supported");
+		return -1;
+	}
+
 	if (params->flags & TLS_CONN_EXT_CERT_CHECK) {
 		wpa_printf(MSG_INFO,
 			   "GnuTLS: tls_ext_cert_check=1 not supported");
@@ -602,6 +611,44 @@
 }
 
 
+#if GNUTLS_VERSION_NUMBER >= 0x030103
+static int server_ocsp_status_req(gnutls_session_t session, void *ptr,
+				  gnutls_datum_t *resp)
+{
+	struct tls_global *global = ptr;
+	char *cached;
+	size_t len;
+
+	if (!global->ocsp_stapling_response) {
+		wpa_printf(MSG_DEBUG, "GnuTLS: OCSP status callback - no response configured");
+		return GNUTLS_E_NO_CERTIFICATE_STATUS;
+	}
+
+	cached = os_readfile(global->ocsp_stapling_response, &len);
+	if (!cached) {
+		wpa_printf(MSG_DEBUG,
+			   "GnuTLS: OCSP status callback - could not read response file (%s)",
+			   global->ocsp_stapling_response);
+		return GNUTLS_E_NO_CERTIFICATE_STATUS;
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "GnuTLS: OCSP status callback - send cached response");
+	resp->data = gnutls_malloc(len);
+	if (!resp->data) {
+		os_free(resp);
+		return GNUTLS_E_MEMORY_ERROR;
+	}
+
+	os_memcpy(resp->data, cached, len);
+	resp->size = len;
+	os_free(cached);
+
+	return GNUTLS_E_SUCCESS;
+}
+#endif /* 3.1.3 */
+
+
 int tls_global_set_params(void *tls_ctx,
 			  const struct tls_connection_params *params)
 {
@@ -696,6 +743,17 @@
 		}
 	}
 
+#if GNUTLS_VERSION_NUMBER >= 0x030103
+	os_free(global->ocsp_stapling_response);
+	if (params->ocsp_stapling_response)
+		global->ocsp_stapling_response =
+			os_strdup(params->ocsp_stapling_response);
+	else
+		global->ocsp_stapling_response = NULL;
+	gnutls_certificate_set_ocsp_status_request_function(
+		global->xcred, server_ocsp_status_req, global);
+#endif /* 3.1.3 */
+
 	global->params_set = 1;
 
 	return 0;
diff --git a/src/crypto/tls_internal.c b/src/crypto/tls_internal.c
index 8b90d56..01a7c97 100644
--- a/src/crypto/tls_internal.c
+++ b/src/crypto/tls_internal.c
@@ -331,6 +331,13 @@
 		return -1;
 	}
 
+	if (params->ocsp_stapling_response)
+		cred->ocsp_stapling_response =
+			os_strdup(params->ocsp_stapling_response);
+	if (params->ocsp_stapling_response_multi)
+		cred->ocsp_stapling_response_multi =
+			os_strdup(params->ocsp_stapling_response_multi);
+
 	return 0;
 #else /* CONFIG_TLS_INTERNAL_SERVER */
 	return -1;
diff --git a/src/crypto/tls_openssl.c b/src/crypto/tls_openssl.c
index 1d75ba7..62277c4 100644
--- a/src/crypto/tls_openssl.c
+++ b/src/crypto/tls_openssl.c
@@ -3890,6 +3890,12 @@
 	if (conn == NULL)
 		return -1;
 
+	if (params->flags & TLS_CONN_REQUIRE_OCSP_ALL) {
+		wpa_printf(MSG_INFO,
+			   "OpenSSL: ocsp=3 not supported");
+		return -1;
+	}
+
 	/*
 	 * If the engine isn't explicitly configured, and any of the
 	 * cert/key fields are actually PKCS#11 URIs, then automatically
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index ed5e4a8..cdd1504 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -1735,9 +1735,6 @@
 	if (nl80211_init_bss(bss))
 		goto failed;
 
-	if (linux_iface_up(drv->global->ioctl_sock, ifname) > 0)
-		drv->start_iface_up = 1;
-
 	if (wpa_driver_nl80211_finish_drv_init(drv, set_addr, 1, driver_params))
 		goto failed;
 
@@ -2240,6 +2237,11 @@
 	if (!bss->if_dynamic && nl80211_get_ifmode(bss) == NL80211_IFTYPE_AP)
 		bss->static_ap = 1;
 
+	if (first &&
+	    nl80211_get_ifmode(bss) != NL80211_IFTYPE_P2P_DEVICE &&
+	    linux_iface_up(drv->global->ioctl_sock, bss->ifname) > 0)
+		drv->start_iface_up = 1;
+
 	if (wpa_driver_nl80211_capa(drv))
 		return -1;
 
diff --git a/src/eap_common/eap_eke_common.c b/src/eap_common/eap_eke_common.c
index 4dfdb3f..6217468 100644
--- a/src/eap_common/eap_eke_common.c
+++ b/src/eap_common/eap_eke_common.c
@@ -44,9 +44,7 @@
 	int dhlen;
 
 	dhlen = eap_eke_dh_len(dhgroup);
-	if (dhlen < 0)
-		return -1;
-	if (encr != EAP_EKE_ENCR_AES128_CBC)
+	if (dhlen < 0 || encr != EAP_EKE_ENCR_AES128_CBC)
 		return -1;
 	return AES_BLOCK_SIZE + dhlen;
 }
@@ -166,14 +164,11 @@
 	size_t pub_len, i;
 
 	generator = eap_eke_dh_generator(group);
-	if (generator < 0 || generator > 255)
+	dh = eap_eke_dh_group(group);
+	if (generator < 0 || generator > 255 || !dh)
 		return -1;
 	gen = generator;
 
-	dh = eap_eke_dh_group(group);
-	if (dh == NULL)
-		return -1;
-
 	/* x = random number 2 .. p-1 */
 	if (random_get_bytes(ret_priv, dh->prime_len))
 		return -1;
@@ -411,11 +406,8 @@
 	size_t len;
 	const struct dh_group *dh;
 
-	if (sess->encr != EAP_EKE_ENCR_AES128_CBC)
-		return -1;
-
 	dh = eap_eke_dh_group(sess->dhgroup);
-	if (dh == NULL)
+	if (sess->encr != EAP_EKE_ENCR_AES128_CBC || !dh)
 		return -1;
 
 	/* Decrypt peer DHComponent */
@@ -635,6 +627,7 @@
 
 	if (*prot_len < block_size + data_len + pad + icv_len) {
 		wpa_printf(MSG_INFO, "EAP-EKE: Not enough room for Prot() data");
+		return -1;
 	}
 	pos = prot;
 
@@ -653,10 +646,8 @@
 		pos += pad;
 	}
 
-	if (aes_128_cbc_encrypt(sess->ke, iv, e, data_len + pad) < 0)
-		return -1;
-
-	if (eap_eke_mac(sess->mac, sess->ki, e, data_len + pad, pos) < 0)
+	if (aes_128_cbc_encrypt(sess->ke, iv, e, data_len + pad) < 0 ||
+	    eap_eke_mac(sess->mac, sess->ki, e, data_len + pad, pos) < 0)
 		return -1;
 	pos += icv_len;
 
@@ -684,9 +675,8 @@
 	else
 		return -1;
 
-	if (prot_len < 2 * block_size + icv_len)
-		return -1;
-	if ((prot_len - icv_len) % block_size)
+	if (prot_len < 2 * block_size + icv_len ||
+	    (prot_len - icv_len) % block_size)
 		return -1;
 
 	if (eap_eke_mac(sess->mac, sess->ki, prot + block_size,
@@ -737,22 +727,14 @@
 	sess->mac = mac;
 
 	sess->prf_len = eap_eke_prf_len(prf);
-	if (sess->prf_len < 0)
-		return -1;
 	sess->nonce_len = eap_eke_nonce_len(prf);
-	if (sess->nonce_len < 0)
-		return -1;
 	sess->auth_len = eap_eke_auth_len(prf);
-	if (sess->auth_len < 0)
-		return -1;
 	sess->dhcomp_len = eap_eke_dhcomp_len(sess->dhgroup, sess->encr);
-	if (sess->dhcomp_len < 0)
-		return -1;
 	sess->pnonce_len = eap_eke_pnonce_len(sess->mac);
-	if (sess->pnonce_len < 0)
-		return -1;
 	sess->pnonce_ps_len = eap_eke_pnonce_ps_len(sess->mac);
-	if (sess->pnonce_ps_len < 0)
+	if (sess->prf_len < 0 || sess->nonce_len < 0 || sess->auth_len < 0 ||
+	    sess->dhcomp_len < 0 || sess->pnonce_len < 0 ||
+	    sess->pnonce_ps_len < 0)
 		return -1;
 
 	return 0;
diff --git a/src/eap_peer/eap_tls_common.c b/src/eap_peer/eap_tls_common.c
index c270832..406c162 100644
--- a/src/eap_peer/eap_tls_common.c
+++ b/src/eap_peer/eap_tls_common.c
@@ -196,8 +196,10 @@
 
 	if (config->ocsp)
 		params->flags |= TLS_CONN_REQUEST_OCSP;
-	if (config->ocsp == 2)
+	if (config->ocsp >= 2)
 		params->flags |= TLS_CONN_REQUIRE_OCSP;
+	if (config->ocsp == 3)
+		params->flags |= TLS_CONN_REQUIRE_OCSP_ALL;
 	data->conn = tls_connection_init(data->ssl_ctx);
 	if (data->conn == NULL) {
 		wpa_printf(MSG_INFO, "SSL: Failed to initialize new TLS "
diff --git a/src/eap_peer/eap_tnc.c b/src/eap_peer/eap_tnc.c
index 25b9f12..e4294bb 100644
--- a/src/eap_peer/eap_tnc.c
+++ b/src/eap_peer/eap_tnc.c
@@ -10,6 +10,7 @@
 
 #include "common.h"
 #include "eap_i.h"
+#include "eap_config.h"
 #include "tncc.h"
 
 
@@ -35,12 +36,16 @@
 static void * eap_tnc_init(struct eap_sm *sm)
 {
 	struct eap_tnc_data *data;
+	struct eap_peer_config *config = eap_get_config(sm);
 
 	data = os_zalloc(sizeof(*data));
 	if (data == NULL)
 		return NULL;
 	data->state = WAIT_START;
-	data->fragment_size = 1300;
+	if (config && config->fragment_size)
+		data->fragment_size = config->fragment_size;
+	else
+		data->fragment_size = 1300;
 	data->tncc = tncc_init();
 	if (data->tncc == NULL) {
 		os_free(data);
@@ -345,11 +350,6 @@
 	ret->decision = DECISION_UNCOND_SUCC;
 	ret->allowNotifications = TRUE;
 
-	if (data->out_buf) {
-		data->state = PROC_MSG;
-		return eap_tnc_build_msg(data, ret, id);
-	}
-
 	if (tncs_done) {
 		resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, 1,
 				     EAP_CODE_RESPONSE, eap_get_id(reqData));
diff --git a/src/eap_peer/tncc.c b/src/eap_peer/tncc.c
index 7ca956e..9965513 100644
--- a/src/eap_peer/tncc.c
+++ b/src/eap_peer/tncc.c
@@ -694,6 +694,8 @@
 	enum tncc_process_res res = TNCCS_PROCESS_OK_NO_RECOMMENDATION;
 	int recommendation_msg = 0;
 
+	wpa_hexdump_ascii(MSG_MSGDUMP, "TNC: Received IF-TNCCS message",
+			  msg, len);
 	buf = dup_binstr(msg, len);
 	if (buf == NULL)
 		return TNCCS_PROCESS_ERROR;
diff --git a/src/radius/radius_client.c b/src/radius/radius_client.c
index 0bcdb45..b24bbf8 100644
--- a/src/radius/radius_client.c
+++ b/src/radius/radius_client.c
@@ -308,7 +308,7 @@
 	int _errno = errno;
 	wpa_printf(MSG_INFO, "send[RADIUS,s=%d]: %s", s, strerror(errno));
 	if (_errno == ENOTCONN || _errno == EDESTADDRREQ || _errno == EINVAL ||
-	    _errno == EBADF || _errno == ENETUNREACH) {
+	    _errno == EBADF || _errno == ENETUNREACH || _errno == EACCES) {
 		hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
 			       HOSTAPD_LEVEL_INFO,
 			       "Send failed - maybe interface status changed -"
diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c
index 669f658..64ef933 100644
--- a/src/rsn_supp/wpa.c
+++ b/src/rsn_supp/wpa.c
@@ -270,7 +270,7 @@
 				 * much we can do here without knowing what
 				 * exactly caused the server to misbehave.
 				 */
-				wpa_dbg(sm->ctx->msg_ctx, MSG_INFO,
+				wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
 					"RSN: PMKID mismatch - authentication server may have derived different MSK?!");
 				return -1;
 			}
diff --git a/src/tls/tlsv1_client.c b/src/tls/tlsv1_client.c
index cc404c1..9bc0d21 100644
--- a/src/tls/tlsv1_client.c
+++ b/src/tls/tlsv1_client.c
@@ -111,7 +111,6 @@
 		pos += conn->rl.iv_size;
 		/* server_write_IV */
 		os_memcpy(conn->rl.read_iv, pos, conn->rl.iv_size);
-		pos += conn->rl.iv_size;
 	} else {
 		/*
 		 * Use IV field to set the mask value for TLS v1.1. A fixed
diff --git a/src/tls/tlsv1_client_ocsp.c b/src/tls/tlsv1_client_ocsp.c
index 2d5cdb9..1d7b68c 100644
--- a/src/tls/tlsv1_client_ocsp.c
+++ b/src/tls/tlsv1_client_ocsp.c
@@ -316,6 +316,7 @@
 
 static enum tls_ocsp_result
 tls_process_ocsp_responses(struct tlsv1_client *conn,
+			   struct x509_certificate *cert,
 			   struct x509_certificate *issuer, const u8 *resp,
 			   size_t len)
 {
@@ -335,8 +336,7 @@
 				   hdr.class, hdr.tag);
 			return TLS_OCSP_INVALID;
 		}
-		if (tls_process_ocsp_single_response(conn, conn->server_cert,
-						     issuer,
+		if (tls_process_ocsp_single_response(conn, cert, issuer,
 						     hdr.payload, hdr.length,
 						     &res) == 0)
 			return res;
@@ -350,8 +350,9 @@
 
 
 static enum tls_ocsp_result
-tls_process_basic_ocsp_response(struct tlsv1_client *conn, const u8 *resp,
-				size_t len)
+tls_process_basic_ocsp_response(struct tlsv1_client *conn,
+				struct x509_certificate *srv_cert,
+				const u8 *resp, size_t len)
 {
 	struct asn1_hdr hdr;
 	const u8 *pos, *end;
@@ -365,6 +366,7 @@
 	struct x509_name name; /* used if key_hash == NULL */
 	char buf[100];
 	os_time_t produced_at;
+	enum tls_ocsp_result res;
 
 	wpa_hexdump(MSG_MSGDUMP, "OCSP: BasicOCSPResponse", resp, len);
 
@@ -594,20 +596,20 @@
 		/* Ignore for now. */
 	}
 
-	if (!conn->server_cert) {
+	if (!srv_cert) {
 		wpa_printf(MSG_DEBUG,
 			   "OCSP: Server certificate not known - cannot check OCSP response");
 		goto no_resp;
 	}
 
-	if (conn->server_cert->next) {
+	if (srv_cert->next) {
 		/* Issuer has already been verified in the chain */
-		issuer = conn->server_cert->next;
+		issuer = srv_cert->next;
 	} else {
 		/* Find issuer from the set of trusted certificates */
 		for (issuer = conn->cred ? conn->cred->trusted_certs : NULL;
 		     issuer; issuer = issuer->next) {
-			if (x509_name_compare(&conn->server_cert->issuer,
+			if (x509_name_compare(&srv_cert->issuer,
 					      &issuer->subject) == 0)
 				break;
 		}
@@ -625,7 +627,7 @@
 	} else {
 		for (signer = certs; signer; signer = signer->next) {
 			if (!ocsp_responder_id_match(signer, &name, key_hash) ||
-			    x509_name_compare(&conn->server_cert->issuer,
+			    x509_name_compare(&srv_cert->issuer,
 					      &issuer->subject) != 0 ||
 			    !(signer->ext_key_usage &
 			      X509_EXT_KEY_USAGE_OCSP) ||
@@ -654,8 +656,13 @@
 		    return TLS_OCSP_INVALID;
 	}
 
-	return tls_process_ocsp_responses(conn, issuer, responses,
-					  responses_len);
+	res = tls_process_ocsp_responses(conn, srv_cert, issuer,
+					 responses, responses_len);
+	if (res == TLS_OCSP_REVOKED)
+		srv_cert->ocsp_revoked = 1;
+	else if (res == TLS_OCSP_GOOD)
+		srv_cert->ocsp_good = 1;
+	return res;
 
 no_resp:
 	x509_free_name(&name);
@@ -677,6 +684,9 @@
 	u8 resp_status;
 	struct asn1_oid oid;
 	char obuf[80];
+	struct x509_certificate *cert;
+	enum tls_ocsp_result res = TLS_OCSP_NO_RESPONSE;
+	enum tls_ocsp_result res_first = res;
 
 	wpa_hexdump(MSG_MSGDUMP, "TLSv1: OCSPResponse", resp, len);
 
@@ -769,5 +779,25 @@
 		return TLS_OCSP_INVALID;
 	}
 
-	return tls_process_basic_ocsp_response(conn, hdr.payload, hdr.length);
+	cert = conn->server_cert;
+	while (cert) {
+		if (!cert->ocsp_good && !cert->ocsp_revoked) {
+			char sbuf[128];
+
+			x509_name_string(&cert->subject, sbuf, sizeof(sbuf));
+			wpa_printf(MSG_DEBUG,
+				   "OCSP: Trying to find certificate status for %s",
+				   sbuf);
+
+			res = tls_process_basic_ocsp_response(conn, cert,
+							      hdr.payload,
+							      hdr.length);
+			if (cert == conn->server_cert)
+				res_first = res;
+		}
+		if (res == TLS_OCSP_REVOKED || cert->issuer_trusted)
+			break;
+		cert = cert->next;
+	}
+	return res == TLS_OCSP_REVOKED ? res : res_first;
 }
diff --git a/src/tls/tlsv1_client_read.c b/src/tls/tlsv1_client_read.c
index ff12452..244c3cb 100644
--- a/src/tls/tlsv1_client_read.c
+++ b/src/tls/tlsv1_client_read.c
@@ -326,7 +326,7 @@
 	union tls_event_data ev;
 	char subject[128];
 
-	if (!conn->event_cb)
+	if (!conn->event_cb || !cert)
 		return;
 
 	os_memset(&ev, 0, sizeof(ev));
@@ -790,14 +790,40 @@
 }
 
 
+static enum tls_ocsp_result
+tls_process_certificate_status_ocsp_response(struct tlsv1_client *conn,
+					     const u8 *pos, size_t len)
+{
+	const u8 *end = pos + len;
+	u32 ocsp_resp_len;
+
+	/* opaque OCSPResponse<1..2^24-1>; */
+	if (end - pos < 3) {
+		wpa_printf(MSG_INFO, "TLSv1: Too short OCSPResponse");
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+		return TLS_OCSP_INVALID;
+	}
+	ocsp_resp_len = WPA_GET_BE24(pos);
+	pos += 3;
+	if (end - pos < ocsp_resp_len) {
+		wpa_printf(MSG_INFO, "TLSv1: Truncated OCSPResponse");
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+		return TLS_OCSP_INVALID;
+	}
+
+	return tls_process_ocsp_response(conn, pos, ocsp_resp_len);
+}
+
+
 static int tls_process_certificate_status(struct tlsv1_client *conn, u8 ct,
 					   const u8 *in_data, size_t *in_len)
 {
 	const u8 *pos, *end;
 	size_t left, len;
 	u8 type, status_type;
-	u32 ocsp_resp_len;
 	enum tls_ocsp_result res;
+	struct x509_certificate *cert;
+	int depth;
 
 	if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
 		wpa_printf(MSG_DEBUG,
@@ -850,6 +876,7 @@
 	 *     CertificateStatusType status_type;
 	 *     select (status_type) {
 	 *         case ocsp: OCSPResponse;
+	 *         case ocsp_multi: OCSPResponseList;
 	 *     } response;
 	 * } CertificateStatus;
 	 */
@@ -862,32 +889,110 @@
 	wpa_printf(MSG_DEBUG, "TLSv1: CertificateStatus status_type %u",
 		   status_type);
 
-	if (status_type != 1 /* ocsp */) {
+	if (status_type == 1 /* ocsp */) {
+		res = tls_process_certificate_status_ocsp_response(
+			conn, pos, end - pos);
+	} else if (status_type == 2 /* ocsp_multi */) {
+		int good = 0, revoked = 0;
+		u32 resp_len;
+
+		res = TLS_OCSP_NO_RESPONSE;
+
+		/*
+		 * opaque OCSPResponse<0..2^24-1>;
+		 *
+		 * struct {
+		 *   OCSPResponse ocsp_response_list<1..2^24-1>;
+		 * } OCSPResponseList;
+		 */
+		if (end - pos < 3) {
+			wpa_printf(MSG_DEBUG,
+				   "TLSv1: Truncated OCSPResponseList");
+			res = TLS_OCSP_INVALID;
+			goto done;
+		}
+		resp_len = WPA_GET_BE24(pos);
+		pos += 3;
+		if (end - pos < resp_len) {
+			wpa_printf(MSG_DEBUG,
+				   "TLSv1: Truncated OCSPResponseList(len=%u)",
+				   resp_len);
+			res = TLS_OCSP_INVALID;
+			goto done;
+		}
+		end = pos + resp_len;
+
+		while (end - pos >= 3) {
+			resp_len = WPA_GET_BE24(pos);
+			pos += 3;
+			if (resp_len > end - pos) {
+				wpa_printf(MSG_DEBUG,
+					   "TLSv1: Truncated OCSPResponse(len=%u; left=%d) in ocsp_multi",
+					   resp_len, (int) (end - pos));
+				res = TLS_OCSP_INVALID;
+				break;
+			}
+			if (!resp_len)
+				continue; /* Skip an empty response */
+			res = tls_process_certificate_status_ocsp_response(
+				conn, pos - 3, resp_len + 3);
+			if (res == TLS_OCSP_REVOKED)
+				revoked++;
+			else if (res == TLS_OCSP_GOOD)
+				good++;
+			pos += resp_len;
+		}
+
+		if (revoked)
+			res = TLS_OCSP_REVOKED;
+		else if (good)
+			res = TLS_OCSP_GOOD;
+	} else {
 		wpa_printf(MSG_DEBUG,
 			   "TLSv1: Ignore unsupported CertificateStatus");
 		goto skip;
 	}
 
-	/* opaque OCSPResponse<1..2^24-1>; */
-	if (end - pos < 3) {
-		wpa_printf(MSG_INFO, "TLSv1: Too short OCSPResponse");
-		tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
-		return -1;
-	}
-	ocsp_resp_len = WPA_GET_BE24(pos);
-	pos += 3;
-	if (end - pos < ocsp_resp_len) {
-		wpa_printf(MSG_INFO, "TLSv1: Truncated OCSPResponse");
-		tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+done:
+	if (res == TLS_OCSP_REVOKED) {
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_CERTIFICATE_REVOKED);
+		for (cert = conn->server_cert, depth = 0; cert;
+		     cert = cert->next, depth++) {
+			if (cert->ocsp_revoked) {
+				tls_cert_chain_failure_event(
+					conn, depth, cert, TLS_FAIL_REVOKED,
+					"certificate revoked");
+			}
+		}
 		return -1;
 	}
 
-	res = tls_process_ocsp_response(conn, pos, ocsp_resp_len);
-	switch (res) {
-	case TLS_OCSP_NO_RESPONSE:
-		if (!(conn->flags & TLS_CONN_REQUIRE_OCSP))
-			goto skip;
+	if (conn->flags & TLS_CONN_REQUIRE_OCSP_ALL) {
+		/*
+		 * Verify that each certificate on the chain that is not part
+		 * of the trusted certificates has a good status. If not,
+		 * terminate handshake.
+		 */
+		for (cert = conn->server_cert, depth = 0; cert;
+		     cert = cert->next, depth++) {
+			if (!cert->ocsp_good) {
+				tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+					  TLS_ALERT_BAD_CERTIFICATE_STATUS_RESPONSE);
+				tls_cert_chain_failure_event(
+					conn, depth, cert,
+					TLS_FAIL_UNSPECIFIED,
+					"bad certificate status response");
+				return -1;
+			}
+			if (cert->issuer_trusted)
+				break;
+		}
+	}
+
+	if ((conn->flags & TLS_CONN_REQUIRE_OCSP) && res != TLS_OCSP_GOOD) {
 		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  res == TLS_OCSP_INVALID ? TLS_ALERT_DECODE_ERROR :
 			  TLS_ALERT_BAD_CERTIFICATE_STATUS_RESPONSE);
 		if (conn->server_cert)
 			tls_cert_chain_failure_event(
@@ -895,28 +1000,8 @@
 				TLS_FAIL_UNSPECIFIED,
 				"bad certificate status response");
 		return -1;
-	case TLS_OCSP_INVALID:
-		if (!(conn->flags & TLS_CONN_REQUIRE_OCSP))
-			goto skip; /* ignore - process as if no response */
-		tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
-		if (conn->server_cert)
-			tls_cert_chain_failure_event(
-				conn, 0, conn->server_cert,
-				TLS_FAIL_UNSPECIFIED,
-				"bad certificate status response");
-		return -1;
-	case TLS_OCSP_GOOD:
-		wpa_printf(MSG_DEBUG, "TLSv1: OCSP response good");
-		break;
-	case TLS_OCSP_REVOKED:
-		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
-			  TLS_ALERT_CERTIFICATE_REVOKED);
-		if (conn->server_cert)
-			tls_cert_chain_failure_event(
-				conn, 0, conn->server_cert, TLS_FAIL_REVOKED,
-				"certificate revoked");
-		return -1;
 	}
+
 	conn->ocsp_resp_received = 1;
 
 skip:
diff --git a/src/tls/tlsv1_client_write.c b/src/tls/tlsv1_client_write.c
index 8e8cb5e..04d895e 100644
--- a/src/tls/tlsv1_client_write.c
+++ b/src/tls/tlsv1_client_write.c
@@ -192,6 +192,46 @@
 		pos += 2;
 		WPA_PUT_BE16(pos, 0); /* request_extensions(empty) */
 		pos += 2;
+
+		wpa_printf(MSG_DEBUG,
+			   "TLSv1: Add status_request_v2 extension for OCSP stapling");
+		/* ExtensionsType extension_type = status_request_v2(17) */
+		WPA_PUT_BE16(pos, TLS_EXT_STATUS_REQUEST_V2);
+		pos += 2;
+		/* opaque extension_data<0..2^16-1> length */
+		WPA_PUT_BE16(pos, 7);
+		pos += 2;
+
+		/*
+		 * RFC 6961, 2.2:
+		 * struct {
+		 *     CertificateStatusType status_type;
+		 *     uint16 request_length;
+		 *     select (status_type) {
+		 *         case ocsp: OCSPStatusRequest;
+		 *         case ocsp_multi: OCSPStatusRequest;
+		 *     } request;
+		 * } CertificateStatusRequestItemV2;
+		 *
+		 * enum { ocsp(1), ocsp_multi(2), (255) } CertificateStatusType;
+		 *
+		 * struct {
+		 * CertificateStatusRequestItemV2
+		 *     certificate_status_req_list<1..2^16-1>;
+		 * } CertificateStatusRequestListV2;
+		 */
+
+		/* certificate_status_req_list<1..2^16-1> */
+		WPA_PUT_BE16(pos, 5);
+		pos += 2;
+
+		/* CertificateStatusRequestItemV2 */
+		*pos++ = 2; /* status_type = ocsp_multi(2) */
+		/* OCSPStatusRequest as shown above for v1 */
+		WPA_PUT_BE16(pos, 0); /* responder_id_list(empty) */
+		pos += 2;
+		WPA_PUT_BE16(pos, 0); /* request_extensions(empty) */
+		pos += 2;
 	}
 
 	if (pos == ext_start + 2)
diff --git a/src/tls/tlsv1_common.h b/src/tls/tlsv1_common.h
index 7a252fe..e30b15a 100644
--- a/src/tls/tlsv1_common.h
+++ b/src/tls/tlsv1_common.h
@@ -170,6 +170,7 @@
 #define TLS_EXT_TRUNCATED_HMAC			4 /* RFC 4366 */
 #define TLS_EXT_STATUS_REQUEST			5 /* RFC 4366 */
 #define TLS_EXT_SIGNATURE_ALGORITHMS		13 /* RFC 5246 */
+#define TLS_EXT_STATUS_REQUEST_V2		17 /* RFC 6961 */
 #define TLS_EXT_SESSION_TICKET			35 /* RFC 4507 */
 
 #define TLS_EXT_PAC_OPAQUE TLS_EXT_SESSION_TICKET /* EAP-FAST terminology */
diff --git a/src/tls/tlsv1_cred.c b/src/tls/tlsv1_cred.c
index 92f97c7..52c1ae0 100644
--- a/src/tls/tlsv1_cred.c
+++ b/src/tls/tlsv1_cred.c
@@ -36,6 +36,8 @@
 	crypto_private_key_free(cred->key);
 	os_free(cred->dh_p);
 	os_free(cred->dh_g);
+	os_free(cred->ocsp_stapling_response);
+	os_free(cred->ocsp_stapling_response_multi);
 	os_free(cred);
 }
 
diff --git a/src/tls/tlsv1_cred.h b/src/tls/tlsv1_cred.h
index b4bfe38..716e93c 100644
--- a/src/tls/tlsv1_cred.h
+++ b/src/tls/tlsv1_cred.h
@@ -24,6 +24,9 @@
 	size_t dh_p_len;
 	u8 *dh_g; /* generator */
 	size_t dh_g_len;
+
+	char *ocsp_stapling_response;
+	char *ocsp_stapling_response_multi;
 };
 
 
diff --git a/src/tls/tlsv1_server_i.h b/src/tls/tlsv1_server_i.h
index 96d79b3..29c6678 100644
--- a/src/tls/tlsv1_server_i.h
+++ b/src/tls/tlsv1_server_i.h
@@ -55,6 +55,9 @@
 	void *log_cb_ctx;
 
 	int use_session_ticket;
+	unsigned int status_request:1;
+	unsigned int status_request_v2:1;
+	unsigned int status_request_multi:1;
 
 	u8 *dh_secret;
 	size_t dh_secret_len;
diff --git a/src/tls/tlsv1_server_read.c b/src/tls/tlsv1_server_read.c
index 8347d7a..4aa8a01 100644
--- a/src/tls/tlsv1_server_read.c
+++ b/src/tls/tlsv1_server_read.c
@@ -46,6 +46,78 @@
 }
 
 
+static void tls_process_status_request_item(struct tlsv1_server *conn,
+					    const u8 *req, size_t req_len)
+{
+	const u8 *pos, *end;
+	u8 status_type;
+
+	pos = req;
+	end = req + req_len;
+
+	/*
+	 * RFC 6961, 2.2:
+	 * struct {
+	 *   CertificateStatusType status_type;
+	 *   uint16 request_length;
+	 *   select (status_type) {
+	 *     case ocsp: OCSPStatusRequest;
+	 *     case ocsp_multi: OCSPStatusRequest;
+	 *   } request;
+	 * } CertificateStatusRequestItemV2;
+	 *
+	 * enum { ocsp(1), ocsp_multi(2), (255) } CertificateStatusType;
+	 */
+
+	if (end - pos < 1)
+		return; /* Truncated data */
+
+	status_type = *pos++;
+	wpa_printf(MSG_DEBUG, "TLSv1: CertificateStatusType %u", status_type);
+	if (status_type != 1 && status_type != 2)
+		return; /* Unsupported status type */
+	/*
+	 * For now, only OCSP stapling is supported, so ignore the specific
+	 * request, if any.
+	 */
+	wpa_hexdump(MSG_DEBUG, "TLSv1: OCSPStatusRequest", pos, end - pos);
+
+	if (status_type == 2)
+		conn->status_request_multi = 1;
+}
+
+
+static void tls_process_status_request_v2(struct tlsv1_server *conn,
+					  const u8 *ext, size_t ext_len)
+{
+	const u8 *pos, *end;
+
+	conn->status_request_v2 = 1;
+
+	pos = ext;
+	end = ext + ext_len;
+
+	/*
+	 * RFC 6961, 2.2:
+	 * struct {
+	 *   CertificateStatusRequestItemV2
+	 *                    certificate_status_req_list<1..2^16-1>;
+	 * } CertificateStatusRequestListV2;
+	 */
+
+	while (end - pos >= 2) {
+		u16 len;
+
+		len = WPA_GET_BE16(pos);
+		pos += 2;
+		if (len > end - pos)
+			break; /* Truncated data */
+		tls_process_status_request_item(conn, pos, len);
+		pos += len;
+	}
+}
+
+
 static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct,
 				    const u8 *in_data, size_t *in_len)
 {
@@ -267,6 +339,11 @@
 						  ext_len);
 					conn->session_ticket_len = ext_len;
 				}
+			} else if (ext_type == TLS_EXT_STATUS_REQUEST) {
+				conn->status_request = 1;
+			} else if (ext_type == TLS_EXT_STATUS_REQUEST_V2) {
+				tls_process_status_request_v2(conn, pos,
+							      ext_len);
 			}
 
 			pos += ext_len;
diff --git a/src/tls/tlsv1_server_write.c b/src/tls/tlsv1_server_write.c
index e7c5e22..bdc6c11 100644
--- a/src/tls/tlsv1_server_write.c
+++ b/src/tls/tlsv1_server_write.c
@@ -42,7 +42,7 @@
 static int tls_write_server_hello(struct tlsv1_server *conn,
 				  u8 **msgpos, u8 *end)
 {
-	u8 *pos, *rhdr, *hs_start, *hs_length;
+	u8 *pos, *rhdr, *hs_start, *hs_length, *ext_start;
 	struct os_time now;
 	size_t rlen;
 
@@ -97,6 +97,32 @@
 	/* CompressionMethod compression_method */
 	*pos++ = TLS_COMPRESSION_NULL;
 
+	/* Extension */
+	ext_start = pos;
+	pos += 2;
+
+	if (conn->status_request) {
+		/* Add a status_request extension with empty extension_data */
+		/* ExtensionsType extension_type = status_request(5) */
+		WPA_PUT_BE16(pos, TLS_EXT_STATUS_REQUEST);
+		pos += 2;
+		/* opaque extension_data<0..2^16-1> length */
+		WPA_PUT_BE16(pos, 0);
+		pos += 2;
+	}
+
+	if (conn->status_request_v2) {
+		/*
+		  Add a status_request_v2 extension with empty extension_data
+		*/
+		/* ExtensionsType extension_type = status_request_v2(17) */
+		WPA_PUT_BE16(pos, TLS_EXT_STATUS_REQUEST_V2);
+		pos += 2;
+		/* opaque extension_data<0..2^16-1> length */
+		WPA_PUT_BE16(pos, 0);
+		pos += 2;
+	}
+
 	if (conn->session_ticket && conn->session_ticket_cb) {
 		int res = conn->session_ticket_cb(
 			conn->session_ticket_cb_ctx,
@@ -133,6 +159,11 @@
 		 */
 	}
 
+	if (pos == ext_start + 2)
+		pos -= 2; /* no extensions */
+	else
+		WPA_PUT_BE16(ext_start, pos - ext_start - 2);
+
 	WPA_PUT_BE24(hs_length, pos - hs_length - 3);
 	tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
 
@@ -244,6 +275,93 @@
 }
 
 
+static int tls_write_server_certificate_status(struct tlsv1_server *conn,
+					       u8 **msgpos, u8 *end,
+					       int ocsp_multi,
+					       char *ocsp_resp,
+					       size_t ocsp_resp_len)
+{
+	u8 *pos, *rhdr, *hs_start, *hs_length;
+	size_t rlen;
+
+	if (!ocsp_resp) {
+		 /*
+		  * Client did not request certificate status or there is no
+		  * matching response cached.
+		  */
+		return 0;
+	}
+
+	pos = *msgpos;
+	if (TLS_RECORD_HEADER_LEN + 1 + 3 + 1 + 3 + ocsp_resp_len >
+	    (unsigned int) (end - pos)) {
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+
+	tlsv1_server_log(conn, "Send CertificateStatus (multi=%d)", ocsp_multi);
+	rhdr = pos;
+	pos += TLS_RECORD_HEADER_LEN;
+
+	/* opaque fragment[TLSPlaintext.length] */
+
+	/* Handshake */
+	hs_start = pos;
+	/* HandshakeType msg_type */
+	*pos++ = TLS_HANDSHAKE_TYPE_CERTIFICATE_STATUS;
+	/* uint24 length (to be filled) */
+	hs_length = pos;
+	pos += 3;
+
+	/* body - CertificateStatus
+	 *
+	 * struct {
+	 *     CertificateStatusType status_type;
+	 *     select (status_type) {
+	 *         case ocsp: OCSPResponse;
+	 *         case ocsp_multi: OCSPResponseList;
+	 *     } response;
+	 * } CertificateStatus;
+	 *
+	 * opaque OCSPResponse<1..2^24-1>;
+	 *
+	 * struct {
+	 *   OCSPResponse ocsp_response_list<1..2^24-1>;
+	 * } OCSPResponseList;
+	 */
+
+	/* CertificateStatusType status_type */
+	if (ocsp_multi)
+		*pos++ = 2; /* ocsp_multi(2) */
+	else
+		*pos++ = 1; /* ocsp(1) */
+	/* uint24 length of OCSPResponse */
+	WPA_PUT_BE24(pos, ocsp_resp_len);
+	pos += 3;
+	os_memcpy(pos, ocsp_resp, ocsp_resp_len);
+	pos += ocsp_resp_len;
+
+	WPA_PUT_BE24(hs_length, pos - hs_length - 3);
+
+	if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
+			      rhdr, end - rhdr, hs_start, pos - hs_start,
+			      &rlen) < 0) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record");
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+	pos = rhdr + rlen;
+
+	tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
+
+	*msgpos = pos;
+
+	return 0;
+}
+
+
 static int tls_write_server_key_exchange(struct tlsv1_server *conn,
 					 u8 **msgpos, u8 *end)
 {
@@ -810,24 +928,46 @@
 {
 	u8 *msg, *end, *pos;
 	size_t msglen;
+	int ocsp_multi = 0;
+	char *ocsp_resp = NULL;
+	size_t ocsp_resp_len = 0;
 
 	*out_len = 0;
 
-	msglen = 1000 + tls_server_cert_chain_der_len(conn);
+	if (conn->status_request_multi &&
+	    conn->cred->ocsp_stapling_response_multi) {
+		ocsp_resp = os_readfile(
+			conn->cred->ocsp_stapling_response_multi,
+			&ocsp_resp_len);
+		ocsp_multi = 1;
+	} else if ((conn->status_request || conn->status_request_v2) &&
+		   conn->cred->ocsp_stapling_response) {
+		ocsp_resp = os_readfile(conn->cred->ocsp_stapling_response,
+					&ocsp_resp_len);
+	}
+	if (!ocsp_resp)
+		ocsp_resp_len = 0;
+
+	msglen = 1000 + tls_server_cert_chain_der_len(conn) + ocsp_resp_len;
 
 	msg = os_malloc(msglen);
-	if (msg == NULL)
+	if (msg == NULL) {
+		os_free(ocsp_resp);
 		return NULL;
+	}
 
 	pos = msg;
 	end = msg + msglen;
 
 	if (tls_write_server_hello(conn, &pos, end) < 0) {
 		os_free(msg);
+		os_free(ocsp_resp);
 		return NULL;
 	}
 
 	if (conn->use_session_ticket) {
+		os_free(ocsp_resp);
+
 		/* Abbreviated handshake using session ticket; RFC 4507 */
 		if (tls_write_server_change_cipher_spec(conn, &pos, end) < 0 ||
 		    tls_write_server_finished(conn, &pos, end) < 0) {
@@ -844,12 +984,16 @@
 
 	/* Full handshake */
 	if (tls_write_server_certificate(conn, &pos, end) < 0 ||
+	    tls_write_server_certificate_status(conn, &pos, end, ocsp_multi,
+						ocsp_resp, ocsp_resp_len) < 0 ||
 	    tls_write_server_key_exchange(conn, &pos, end) < 0 ||
 	    tls_write_server_certificate_request(conn, &pos, end) < 0 ||
 	    tls_write_server_hello_done(conn, &pos, end) < 0) {
 		os_free(msg);
+		os_free(ocsp_resp);
 		return NULL;
 	}
+	os_free(ocsp_resp);
 
 	*out_len = pos - msg;
 
diff --git a/src/tls/x509v3.c b/src/tls/x509v3.c
index 5521390..75f222c 100644
--- a/src/tls/x509v3.c
+++ b/src/tls/x509v3.c
@@ -1339,6 +1339,7 @@
 	size_t left;
 	char sbuf[128];
 	unsigned long value;
+	const u8 *subject_dn;
 
 	/* tbsCertificate TBSCertificate ::= SEQUENCE */
 	if (asn1_get_next(buf, len, &hdr) < 0 ||
@@ -1436,7 +1437,6 @@
 		return -1;
 
 	/* subject Name */
-	const u8 *subject_dn;
 	subject_dn = pos;
 	if (x509_parse_name(pos, end - pos, &cert->subject, &pos))
 		return -1;
@@ -2038,6 +2038,7 @@
 	os_get_time(&now);
 
 	for (cert = chain, idx = 0; cert; cert = cert->next, idx++) {
+		cert->issuer_trusted = 0;
 		x509_name_string(&cert->subject, buf, sizeof(buf)); 
 		wpa_printf(MSG_DEBUG, "X509: %lu: %s", idx, buf);
 
@@ -2123,6 +2124,7 @@
 
 			wpa_printf(MSG_DEBUG, "X509: Trusted certificate "
 				   "found to complete the chain");
+			cert->issuer_trusted = 1;
 			chain_trusted = 1;
 		}
 	}
diff --git a/src/tls/x509v3.h b/src/tls/x509v3.h
index dcdb4a3..7df8e2a 100644
--- a/src/tls/x509v3.h
+++ b/src/tls/x509v3.h
@@ -106,6 +106,11 @@
 	size_t cert_len;
 	const u8 *tbs_cert_start;
 	size_t tbs_cert_len;
+
+	/* Meta data used for certificate validation */
+	unsigned int ocsp_good:1;
+	unsigned int ocsp_revoked:1;
+	unsigned int issuer_trusted:1;
 };
 
 enum {