Cumulative patch from commit 324ade51e168f28430f4429849becd0f08d507c0

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