Client-side OCSP stapling support.

Remove the old implementation which was excessively general. This mirrors the
SCT support and adds a single boolean flag to request an OCSP response with no
responder IDs, extensions, or frills. The response, if received, is stored on
the SSL_SESSION so that it is available for (re)validation on session
resumption; Chromium revalidates the saved auth parameters on resume.

Server support is unimplemented for now. This API will also need to be adjusted
in the future if we implement RFC 6961.

Change-Id: I533c029b7f7ea622d814d05f934fdace2da85cb1
Reviewed-on: https://boringssl-review.googlesource.com/1671
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h
index 0100fb9..70afbd4 100644
--- a/include/openssl/ssl.h
+++ b/include/openssl/ssl.h
@@ -382,6 +382,7 @@
  *	Peer SHA256 [13]        EXPLICIT OCTET STRING, -- optional SHA256 hash of Peer certifiate
  *	original handshake hash [14] EXPLICIT OCTET STRING, -- optional original handshake hash
  *	tlsext_signed_cert_timestamp_list [15] EXPLICIT OCTET STRING, -- optional signed cert timestamp list extension
+ *	ocsp_response [16] EXPLICIT OCTET STRING, -- optional saved OCSP response from the server
  *	}
  * Look in ssl/ssl_asn1.c for more details
  * I'm using EXPLICIT tags so I can read the damn things using asn1parse :-).
@@ -449,8 +450,14 @@
 	uint8_t *tlsext_tick;	/* Session ticket */
 	size_t tlsext_ticklen;		/* Session ticket length */
 	uint32_t tlsext_tick_lifetime_hint;	/* Session lifetime hint in seconds */
+
 	size_t tlsext_signed_cert_timestamp_list_length;
 	uint8_t *tlsext_signed_cert_timestamp_list; /* Server's list. */
+
+	/* The OCSP response that came with the session. */
+	size_t ocsp_response_length;
+	uint8_t *ocsp_response;
+
 	char peer_sha256_valid;		/* Non-zero if peer_sha256 is valid */
 	unsigned char peer_sha256[SHA256_DIGEST_LENGTH];  /* SHA256 of peer certificate */
 
@@ -1023,6 +1030,9 @@
 
 	/* If true, a client will request certificate timestamps. */
 	char signed_cert_timestamps_enabled;
+
+	/* If true, a client will request a stapled OCSP response. */
+	char ocsp_stapling_enabled;
 	};
 
 #endif
@@ -1099,6 +1109,15 @@
  * client SSL objects created from |ctx|. */
 OPENSSL_EXPORT void SSL_CTX_enable_signed_cert_timestamps(SSL_CTX *ctx);
 
+/* SSL_enable_signed_cert_timestamps causes |ssl| (which must be the client end
+ * of a connection) to request a stapled OCSP response from the server. Returns
+ * 1 on success. */
+OPENSSL_EXPORT int SSL_enable_ocsp_stapling(SSL *ssl);
+
+/* SSL_CTX_enable_ocsp_stapling enables OCSP stapling on all client SSL objects
+ * created from |ctx|. */
+OPENSSL_EXPORT void SSL_CTX_enable_ocsp_stapling(SSL_CTX *ctx);
+
 /* SSL_get0_signed_cert_timestamp_list sets |*out| and |*out_len| to point to
  * |*out_len| bytes of SCT information from the server. This is only valid if
  * |ssl| is a client. The SCT information is a SignedCertificateTimestampList
@@ -1109,6 +1128,13 @@
  * WARNING: the returned data is not guaranteed to be well formed. */
 OPENSSL_EXPORT void SSL_get0_signed_cert_timestamp_list(const SSL *ssl, uint8_t **out, size_t *out_len);
 
+/* SSL_get0_ocsp_response sets |*out| and |*out_len| to point to |*out_len|
+ * bytes of an OCSP response from the server. This is the DER encoding of an
+ * OCSPResponse type as defined in RFC 2560.
+ *
+ * WARNING: the returned data is not guaranteed to be well formed. */
+OPENSSL_EXPORT void SSL_get0_ocsp_response(const SSL *ssl, uint8_t **out, size_t *out_len);
+
 OPENSSL_EXPORT void SSL_CTX_set_next_protos_advertised_cb(SSL_CTX *s,
 					   int (*cb) (SSL *ssl,
 						      const unsigned char **out,
@@ -1354,17 +1380,6 @@
 	                          1 : prepare 2, allow last ack just after in server callback.
 	                          2 : don't call servername callback, no ack in server hello
 	                       */
-	/* certificate status request info */
-	/* Status type or -1 if no status type */
-	int tlsext_status_type;
-	/* Expect OCSP CertificateStatus message */
-	int tlsext_status_expected;
-	/* OCSP status request only */
-	STACK_OF(OCSP_RESPID) *tlsext_ocsp_ids;
-	X509_EXTENSIONS *tlsext_ocsp_exts;
-	/* OCSP response received or to be sent */
-	uint8_t *tlsext_ocsp_resp;
-	int tlsext_ocsp_resplen;
 
 	/* RFC4507 session ticket expected to be received or sent */
 	int tlsext_ticket_expected;
@@ -1409,6 +1424,11 @@
 	/* Enable signed certificate time stamps. Currently client only. */
 	char signed_cert_timestamps_enabled;
 
+	/* Enable OCSP stapling. Currently client only.
+	 * TODO(davidben): Add a server-side implementation when it becomes
+	 * necesary. */
+	char ocsp_stapling_enabled;
+
 	/* For a client, this contains the list of supported protocols in wire
 	 * format. */
 	unsigned char* alpn_client_proto_list;
@@ -1648,13 +1668,6 @@
 #define SSL_CTRL_SET_TLSEXT_TICKET_KEYS		59
 #define SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB	63
 #define SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB_ARG	64
-#define SSL_CTRL_SET_TLSEXT_STATUS_REQ_TYPE	65
-#define SSL_CTRL_GET_TLSEXT_STATUS_REQ_EXTS	66
-#define SSL_CTRL_SET_TLSEXT_STATUS_REQ_EXTS	67
-#define SSL_CTRL_GET_TLSEXT_STATUS_REQ_IDS	68
-#define SSL_CTRL_SET_TLSEXT_STATUS_REQ_IDS	69
-#define SSL_CTRL_GET_TLSEXT_STATUS_REQ_OCSP_RESP	70
-#define SSL_CTRL_SET_TLSEXT_STATUS_REQ_OCSP_RESP	71
 
 #define SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB	72
 
diff --git a/include/openssl/ssl3.h b/include/openssl/ssl3.h
index 2e9b8cc..44f9367 100644
--- a/include/openssl/ssl3.h
+++ b/include/openssl/ssl3.h
@@ -469,6 +469,11 @@
 		/* Server-only: cert_request is true if a client certificate was
 		 * requested. */
 		int cert_request;
+
+		/* certificate_status_expected is true if OCSP stapling was
+		 * negotiated and the server is expected to send a
+		 * CertificateStatus message. */
+		char certificate_status_expected;
 		} tmp;
 
         /* Connection binding to prevent renegotiation attacks */
diff --git a/include/openssl/tls1.h b/include/openssl/tls1.h
index b8dd7ae..08ad8e8 100644
--- a/include/openssl/tls1.h
+++ b/include/openssl/tls1.h
@@ -333,27 +333,6 @@
 #define SSL_set_tlsext_debug_arg(ssl, arg) \
 SSL_ctrl(ssl,SSL_CTRL_SET_TLSEXT_DEBUG_ARG,0, (void *)arg)
 
-#define SSL_set_tlsext_status_type(ssl, type) \
-SSL_ctrl(ssl,SSL_CTRL_SET_TLSEXT_STATUS_REQ_TYPE,type, NULL)
-
-#define SSL_get_tlsext_status_exts(ssl, arg) \
-SSL_ctrl(ssl,SSL_CTRL_GET_TLSEXT_STATUS_REQ_EXTS,0, (void *)arg)
-
-#define SSL_set_tlsext_status_exts(ssl, arg) \
-SSL_ctrl(ssl,SSL_CTRL_SET_TLSEXT_STATUS_REQ_EXTS,0, (void *)arg)
-
-#define SSL_get_tlsext_status_ids(ssl, arg) \
-SSL_ctrl(ssl,SSL_CTRL_GET_TLSEXT_STATUS_REQ_IDS,0, (void *)arg)
-
-#define SSL_set_tlsext_status_ids(ssl, arg) \
-SSL_ctrl(ssl,SSL_CTRL_SET_TLSEXT_STATUS_REQ_IDS,0, (void *)arg)
-
-#define SSL_get_tlsext_status_ocsp_resp(ssl, arg) \
-SSL_ctrl(ssl,SSL_CTRL_GET_TLSEXT_STATUS_REQ_OCSP_RESP,0, (void *)arg)
-
-#define SSL_set_tlsext_status_ocsp_resp(ssl, arg, arglen) \
-SSL_ctrl(ssl,SSL_CTRL_SET_TLSEXT_STATUS_REQ_OCSP_RESP,arglen, (void *)arg)
-
 #define SSL_CTX_set_tlsext_servername_callback(ctx, cb) \
 SSL_CTX_callback_ctrl(ctx,SSL_CTRL_SET_TLSEXT_SERVERNAME_CB,(void (*)(void))cb)
 
diff --git a/ssl/d1_clnt.c b/ssl/d1_clnt.c
index 4fcf0c3..b781d88 100644
--- a/ssl/d1_clnt.c
+++ b/ssl/d1_clnt.c
@@ -309,7 +309,7 @@
 				{
 				ret=ssl3_get_server_certificate(s);
 				if (ret <= 0) goto end;
-				if (s->tlsext_status_expected)
+				if (s->s3->tmp.certificate_status_expected)
 					s->state=SSL3_ST_CR_CERT_STATUS_A;
 				else
 					s->state=SSL3_ST_CR_KEY_EXCH_A;
diff --git a/ssl/d1_srvr.c b/ssl/d1_srvr.c
index 102d442..79da484 100644
--- a/ssl/d1_srvr.c
+++ b/ssl/d1_srvr.c
@@ -359,7 +359,7 @@
 				dtls1_start_timer(s);
 				ret=ssl3_send_server_certificate(s);
 				if (ret <= 0) goto end;
-				if (s->tlsext_status_expected)
+				if (s->s3->tmp.certificate_status_expected)
 					s->state=SSL3_ST_SW_CERT_STATUS_A;
 				else
 					s->state=SSL3_ST_SW_KEY_EXCH_A;
@@ -522,6 +522,8 @@
 			s->init_num=0;
 			break;
 
+#if 0
+		// TODO(davidben): Implement OCSP stapling on the server.
 		case SSL3_ST_SW_CERT_STATUS_A:
 		case SSL3_ST_SW_CERT_STATUS_B:
 			ret=ssl3_send_cert_status(s);
@@ -529,7 +531,7 @@
 			s->state=SSL3_ST_SW_KEY_EXCH_A;
 			s->init_num=0;
 			break;
-
+#endif
 
 		case SSL3_ST_SW_CHANGE_A:
 		case SSL3_ST_SW_CHANGE_B:
diff --git a/ssl/s3_clnt.c b/ssl/s3_clnt.c
index 63fbbeb..44d26a7 100644
--- a/ssl/s3_clnt.c
+++ b/ssl/s3_clnt.c
@@ -299,7 +299,7 @@
 				{
 				ret=ssl3_get_server_certificate(s);
 				if (ret <= 0) goto end;
-				if (s->tlsext_status_expected)
+				if (s->s3->tmp.certificate_status_expected)
 					s->state=SSL3_ST_CR_CERT_STATUS_A;
 				else
 					s->state=SSL3_ST_CR_KEY_EXCH_A;
@@ -1798,7 +1798,6 @@
 	long n;
 	CBS certificate_status, ocsp_response;
 	uint8_t status_type;
-	size_t resplen;
 
 	n=s->method->ssl_get_message(s,
 		SSL3_ST_CR_CERT_STATUS_A,
@@ -1822,34 +1821,13 @@
 		goto f_err;
 		}
 
-	/* TODO(davidben): Make tlsext_ocsp_resplen a
-	 * size_t. Currently it uses -1 to signal no response. The
-	 * spec does not allow ocsp_response to be zero-length, so
-	 * using 0 should be fine. */
-	if (!CBS_stow(&ocsp_response, &s->tlsext_ocsp_resp, &resplen))
+	if (!CBS_stow(&ocsp_response,
+			&s->session->ocsp_response, &s->session->ocsp_response_length))
 		{
 		al = SSL_AD_INTERNAL_ERROR;
 		OPENSSL_PUT_ERROR(SSL, ssl3_get_cert_status, ERR_R_MALLOC_FAILURE);
 		goto f_err;
 		}
-	s->tlsext_ocsp_resplen = resplen;
-	if (s->ctx->tlsext_status_cb)
-		{
-		int ret;
-		ret = s->ctx->tlsext_status_cb(s, s->ctx->tlsext_status_arg);
-		if (ret == 0)
-			{
-			al = SSL_AD_BAD_CERTIFICATE_STATUS_RESPONSE;
-			OPENSSL_PUT_ERROR(SSL, ssl3_get_cert_status, SSL_R_INVALID_STATUS_RESPONSE);
-			goto f_err;
-			}
-		if (ret < 0)
-			{
-			al = SSL_AD_INTERNAL_ERROR;
-			OPENSSL_PUT_ERROR(SSL, ssl3_get_cert_status, ERR_R_MALLOC_FAILURE);
-			goto f_err;
-			}
-		}
 	return 1;
 f_err:
 	ssl3_send_alert(s,SSL3_AL_FATAL,al);
diff --git a/ssl/s3_lib.c b/ssl/s3_lib.c
index 50aa4bd..479b6de 100644
--- a/ssl/s3_lib.c
+++ b/ssl/s3_lib.c
@@ -1252,44 +1252,6 @@
 		ret = 1;
 		break;
 
-	case SSL_CTRL_SET_TLSEXT_STATUS_REQ_TYPE:
-		s->tlsext_status_type=larg;
-		ret = 1;
-		break;
-
-	case SSL_CTRL_GET_TLSEXT_STATUS_REQ_EXTS:
-		*(STACK_OF(X509_EXTENSION) **)parg = s->tlsext_ocsp_exts;
-		ret = 1;
-		break;
-
-	case SSL_CTRL_SET_TLSEXT_STATUS_REQ_EXTS:
-		s->tlsext_ocsp_exts = parg;
-		ret = 1;
-		break;
-
-	case SSL_CTRL_GET_TLSEXT_STATUS_REQ_IDS:
-		*(STACK_OF(OCSP_RESPID) **)parg = s->tlsext_ocsp_ids;
-		ret = 1;
-		break;
-
-	case SSL_CTRL_SET_TLSEXT_STATUS_REQ_IDS:
-		s->tlsext_ocsp_ids = parg;
-		ret = 1;
-		break;
-
-	case SSL_CTRL_GET_TLSEXT_STATUS_REQ_OCSP_RESP:
-		*(unsigned char **)parg = s->tlsext_ocsp_resp;
-		return s->tlsext_ocsp_resplen;
-		
-	case SSL_CTRL_SET_TLSEXT_STATUS_REQ_OCSP_RESP:
-		if (s->tlsext_ocsp_resp)
-			OPENSSL_free(s->tlsext_ocsp_resp);
-		s->tlsext_ocsp_resp = parg;
-		s->tlsext_ocsp_resplen = larg;
-		ret = 1;
-		break;
-
-
 	case SSL_CTRL_CHAIN:
 		if (larg)
 			return ssl_cert_set1_chain(s->cert,
diff --git a/ssl/s3_srvr.c b/ssl/s3_srvr.c
index 1017f4a..0e9c7d5 100644
--- a/ssl/s3_srvr.c
+++ b/ssl/s3_srvr.c
@@ -352,7 +352,7 @@
 				{
 				ret=ssl3_send_server_certificate(s);
 				if (ret <= 0) goto end;
-				if (s->tlsext_status_expected)
+				if (s->s3->tmp.certificate_status_expected)
 					s->state=SSL3_ST_SW_CERT_STATUS_A;
 				else
 					s->state=SSL3_ST_SW_KEY_EXCH_A;
@@ -569,6 +569,8 @@
 			s->init_num=0;
 			break;
 
+#if 0
+		// TODO(davidben): Implement OCSP stapling on the server.
 		case SSL3_ST_SW_CERT_STATUS_A:
 		case SSL3_ST_SW_CERT_STATUS_B:
 			ret=ssl3_send_cert_status(s);
@@ -576,6 +578,7 @@
 			s->state=SSL3_ST_SW_KEY_EXCH_A;
 			s->init_num=0;
 			break;
+#endif
 
 		case SSL3_ST_SW_CHANGE_A:
 		case SSL3_ST_SW_CHANGE_B:
@@ -1137,16 +1140,6 @@
 	 * s->tmp.new_cipher	- the new cipher to use.
 	 */
 
-	/* Handles TLS extensions that we couldn't check earlier */
-	if (s->version >= SSL3_VERSION)
-		{
-		if (ssl_check_clienthello_tlsext_late(s) <= 0)
-			{
-			OPENSSL_PUT_ERROR(SSL, ssl3_get_client_hello, SSL_R_CLIENTHELLO_TLSEXT);
-			goto err;
-			}
-		}
-
 	if (ret < 0) ret=-ret;
 	if (0)
 		{
@@ -2651,6 +2644,7 @@
 	return ssl_do_write(s);
 	}
 
+#if 0
 int ssl3_send_cert_status(SSL *s)
 	{
 	if (s->state == SSL3_ST_SW_CERT_STATUS_A)
@@ -2685,6 +2679,7 @@
 	/* SSL3_ST_SW_CERT_STATUS_B */
 	return(ssl3_do_write(s,SSL3_RT_HANDSHAKE));
 	}
+#endif
 
 /* ssl3_get_next_proto reads a Next Protocol Negotiation handshake message. It
  * sets the next_proto member in s if found */
diff --git a/ssl/ssl_asn1.c b/ssl/ssl_asn1.c
index 05c6462..8acd0eb 100644
--- a/ssl/ssl_asn1.c
+++ b/ssl/ssl_asn1.c
@@ -120,12 +120,13 @@
 	ASN1_OCTET_STRING peer_sha256;
 	ASN1_OCTET_STRING original_handshake_hash;
 	ASN1_OCTET_STRING tlsext_signed_cert_timestamp_list;
+	ASN1_OCTET_STRING ocsp_response;
 	} SSL_SESSION_ASN1;
 
 int i2d_SSL_SESSION(SSL_SESSION *in, unsigned char **pp)
 	{
 #define LSIZE2 (sizeof(long)*2)
-	int v1=0,v2=0,v3=0,v4=0,v5=0,v7=0,v8=0,v13=0,v14=0,v15=0;
+	int v1=0,v2=0,v3=0,v4=0,v5=0,v7=0,v8=0,v13=0,v14=0,v15=0,v16=0;
 	unsigned char buf[4],ibuf1[LSIZE2],ibuf2[LSIZE2];
 	unsigned char ibuf3[LSIZE2],ibuf4[LSIZE2],ibuf5[LSIZE2];
 	int v6=0,v9=0,v10=0;
@@ -269,6 +270,13 @@
 				in->tlsext_signed_cert_timestamp_list;
 		}
 
+	if (in->ocsp_response_length > 0)
+		{
+		a.ocsp_response.length = in->ocsp_response_length;
+		a.ocsp_response.type = V_ASN1_OCTET_STRING;
+		a.ocsp_response.data = in->ocsp_response;
+		}
+
 	M_ASN1_I2D_len(&(a.version),		i2d_ASN1_INTEGER);
 	M_ASN1_I2D_len(&(a.ssl_version),	i2d_ASN1_INTEGER);
 	M_ASN1_I2D_len(&(a.cipher),		i2d_ASN1_OCTET_STRING);
@@ -303,6 +311,8 @@
 	if (in->tlsext_signed_cert_timestamp_list_length > 0)
 		M_ASN1_I2D_len_EXP_opt(&(a.tlsext_signed_cert_timestamp_list),
 				i2d_ASN1_OCTET_STRING, 15, v15);
+	if (in->ocsp_response_length > 0)
+		M_ASN1_I2D_len_EXP_opt(&(a.ocsp_response), i2d_ASN1_OCTET_STRING, 16, v16);
 
 	M_ASN1_I2D_seq_total();
 
@@ -340,6 +350,8 @@
 	if (in->tlsext_signed_cert_timestamp_list_length > 0)
 		M_ASN1_I2D_put_EXP_opt(&(a.tlsext_signed_cert_timestamp_list),
 				i2d_ASN1_OCTET_STRING, 15, v15);
+	if (in->ocsp_response > 0)
+		M_ASN1_I2D_put_EXP_opt(&(a.ocsp_response), i2d_ASN1_OCTET_STRING, 16, v16);
 
 	M_ASN1_I2D_finish();
 	}
@@ -600,6 +612,18 @@
 		os.data = NULL;
 		}
 
+	os.length = 0;
+	os.data = NULL;
+	M_ASN1_D2I_get_EXP_opt(osp, d2i_ASN1_OCTET_STRING, 16);
+	if (os.data)
+		{
+		if (ret->ocsp_response)
+			OPENSSL_free(ret->ocsp_response);
+		ret->ocsp_response = os.data;
+		ret->ocsp_response_length = os.length;
+		os.data = NULL;
+		}
+
 
 	M_ASN1_D2I_Finish(a,SSL_SESSION_free,SSL_F_D2I_SSL_SESSION);
 	}
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index 01d59c4..58684b0 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -336,12 +336,6 @@
 	s->tlsext_debug_cb = 0;
 	s->tlsext_debug_arg = NULL;
 	s->tlsext_ticket_expected = 0;
-	s->tlsext_status_type = -1;
-	s->tlsext_status_expected = 0;
-	s->tlsext_ocsp_ids = NULL;
-	s->tlsext_ocsp_exts = NULL;
-	s->tlsext_ocsp_resp = NULL;
-	s->tlsext_ocsp_resplen = -1;
 	CRYPTO_add(&ctx->references,1,CRYPTO_LOCK_SSL_CTX);
 	s->initial_ctx=ctx;
 	if (ctx->tlsext_ecpointformatlist)
@@ -401,7 +395,10 @@
 	s->psk_server_callback=ctx->psk_server_callback;
 
 	if (!s->server)
+		{
 		s->signed_cert_timestamps_enabled = s->ctx->signed_cert_timestamps_enabled;
+		s->ocsp_stapling_enabled = s->ctx->ocsp_stapling_enabled;
+		}
 
 	return(s);
 err:
@@ -670,16 +667,6 @@
 	if (s->initial_ctx) SSL_CTX_free(s->initial_ctx);
 	if (s->tlsext_ecpointformatlist) OPENSSL_free(s->tlsext_ecpointformatlist);
 	if (s->tlsext_ellipticcurvelist) OPENSSL_free(s->tlsext_ellipticcurvelist);
-	if (s->tlsext_ocsp_exts)
-		sk_X509_EXTENSION_pop_free(s->tlsext_ocsp_exts,
-						X509_EXTENSION_free);
-        /* TODO(fork): OCSP support */
-#if 0
-	if (s->tlsext_ocsp_ids)
-		sk_OCSP_RESPID_pop_free(s->tlsext_ocsp_ids, OCSP_RESPID_free);
-#endif
-	if (s->tlsext_ocsp_resp)
-		OPENSSL_free(s->tlsext_ocsp_resp);
 	if (s->alpn_client_proto_list)
 		OPENSSL_free(s->alpn_client_proto_list);
 	if (s->tlsext_channel_id_private)
@@ -1677,7 +1664,7 @@
 
 int SSL_enable_signed_cert_timestamps(SSL *ssl)
 	{
-	/* Currently not implemented server side */
+	/* Currently not implemented server-side. */
 	if (ssl->server)
 		return 0;
 
@@ -1685,6 +1672,20 @@
 	return 1;
 	}
 
+void SSL_CTX_enable_ocsp_stapling(SSL_CTX *ctx)
+	{
+	ctx->ocsp_stapling_enabled = 1;
+	}
+
+int SSL_enable_ocsp_stapling(SSL *ssl)
+	{
+	/* Currently not implemented server-side. */
+	if (ssl->server)
+		return 0;
+	ssl->ocsp_stapling_enabled = 1;
+	return 1;
+	}
+
 void SSL_get0_signed_cert_timestamp_list(const SSL *ssl, uint8_t **out, size_t *out_len)
 	{
 	SSL_SESSION *session = ssl->session;
@@ -1699,6 +1700,20 @@
 	*out_len = session->tlsext_signed_cert_timestamp_list_length;
 	}
 
+void SSL_get0_ocsp_response(const SSL *ssl, uint8_t **out, size_t *out_len)
+	{
+	SSL_SESSION *session = ssl->session;
+
+	*out_len = 0;
+	*out = NULL;
+	if (ssl->server)
+		return;
+	if (!session || !session->ocsp_response)
+		return;
+	*out = session->ocsp_response;
+	*out_len = session->ocsp_response_length;
+	}
+
 /* SSL_select_next_proto implements the standard protocol selection. It is
  * expected that this function is called from the callback set by
  * SSL_CTX_set_next_proto_select_cb.
diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h
index 0aefaa4..ad2d843 100644
--- a/ssl/ssl_locl.h
+++ b/ssl/ssl_locl.h
@@ -1069,7 +1069,6 @@
 unsigned char *ssl_add_clienthello_tlsext(SSL *s, unsigned char *buf, unsigned char *limit, size_t header_len);
 unsigned char *ssl_add_serverhello_tlsext(SSL *s, unsigned char *buf, unsigned char *limit); 
 int ssl_parse_clienthello_tlsext(SSL *s, CBS *cbs);
-int ssl_check_clienthello_tlsext_late(SSL *s);
 int ssl_parse_serverhello_tlsext(SSL *s, CBS *cbs);
 int ssl_prepare_clienthello_tlsext(SSL *s);
 int ssl_prepare_serverhello_tlsext(SSL *s);
diff --git a/ssl/ssl_sess.c b/ssl/ssl_sess.c
index c48c64b..7fd1724 100644
--- a/ssl/ssl_sess.c
+++ b/ssl/ssl_sess.c
@@ -716,6 +716,8 @@
 	if (ss->tlsext_ellipticcurvelist != NULL) OPENSSL_free(ss->tlsext_ellipticcurvelist);
 	if (ss->tlsext_signed_cert_timestamp_list != NULL)
 		OPENSSL_free(ss->tlsext_signed_cert_timestamp_list);
+	if (ss->ocsp_response != NULL)
+		OPENSSL_free(ss->ocsp_response);
 	if (ss->psk_identity_hint != NULL)
 		OPENSSL_free(ss->psk_identity_hint);
 	if (ss->psk_identity != NULL)
diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c
index cafcfa2..8fdd813 100644
--- a/ssl/t1_lib.c
+++ b/ssl/t1_lib.c
@@ -121,8 +121,8 @@
 static int tls_decrypt_ticket(SSL *s, const unsigned char *tick, int ticklen,
 				const unsigned char *sess_id, int sesslen,
 				SSL_SESSION **psess);
-static int ssl_check_clienthello_tlsext_early(SSL *s);
-int ssl_check_serverhello_tlsext(SSL *s);
+static int ssl_check_clienthello_tlsext(SSL *s);
+static int ssl_check_serverhello_tlsext(SSL *s);
 
 SSL3_ENC_METHOD TLSv1_enc_data={
 	tls1_enc,
@@ -1025,56 +1025,23 @@
 		ret += salglen;
 		}
 
-        /* TODO(fork): we probably want OCSP stapling, but it currently pulls in a lot of code. */
-#if 0
-	if (s->tlsext_status_type == TLSEXT_STATUSTYPE_ocsp)
+	if (s->ocsp_stapling_enabled)
 		{
-		int i;
-		long extlen, idlen, itmp;
-		OCSP_RESPID *id;
+		/* The status_request extension is excessively extensible at
+		 * every layer. On the client, only support requesting OCSP
+		 * responses with an empty responder_id_list and no
+		 * extensions. */
+		if (limit - ret - 4 - 1 - 2 - 2 < 0) return NULL;
 
-		idlen = 0;
-		for (i = 0; i < sk_OCSP_RESPID_num(s->tlsext_ocsp_ids); i++)
-			{
-			id = sk_OCSP_RESPID_value(s->tlsext_ocsp_ids, i);
-			itmp = i2d_OCSP_RESPID(id, NULL);
-			if (itmp <= 0)
-				return NULL;
-			idlen += itmp + 2;
-			}
-
-		if (s->tlsext_ocsp_exts)
-			{
-			extlen = i2d_X509_EXTENSIONS(s->tlsext_ocsp_exts, NULL);
-			if (extlen < 0)
-				return NULL;
-			}
-		else
-			extlen = 0;
-			
-		if ((long)(limit - ret - 7 - extlen - idlen) < 0) return NULL;
 		s2n(TLSEXT_TYPE_status_request, ret);
-		if (extlen + idlen > 0xFFF0)
-			return NULL;
-		s2n(extlen + idlen + 5, ret);
+		s2n(1 + 2 + 2, ret);
+		/* status_type */
 		*(ret++) = TLSEXT_STATUSTYPE_ocsp;
-		s2n(idlen, ret);
-		for (i = 0; i < sk_OCSP_RESPID_num(s->tlsext_ocsp_ids); i++)
-			{
-			/* save position of id len */
-			unsigned char *q = ret;
-			id = sk_OCSP_RESPID_value(s->tlsext_ocsp_ids, i);
-			/* skip over id len */
-			ret += 2;
-			itmp = i2d_OCSP_RESPID(id, &ret);
-			/* write id len */
-			s2n(itmp, q);
-			}
-		s2n(extlen, ret);
-		if (extlen > 0)
-			i2d_X509_EXTENSIONS(s->tlsext_ocsp_exts, &ret);
+		/* responder_id_list - empty */
+		s2n(0, ret);
+		/* request_extensions - empty */
+		s2n(0, ret);
 		}
-#endif
 
 	if (s->ctx->next_proto_select_cb && !s->s3->tmp.finish_md_len)
 		{
@@ -1311,7 +1278,7 @@
 		s2n(0,ret);
 		}
 
-	if (s->tlsext_status_expected)
+	if (s->s3->tmp.certificate_status_expected)
 		{ 
 		if ((long)(limit - ret - 4) < 0) return NULL; 
 		s2n(TLSEXT_TYPE_status_request,ret);
@@ -1452,8 +1419,8 @@
 	size_t i;
 
 	s->servername_done = 0;
-	s->tlsext_status_type = -1;
 	s->s3->next_proto_neg_seen = 0;
+	s->s3->tmp.certificate_status_expected = 0;
 
 	if (s->s3->alpn_selected)
 		{
@@ -1480,23 +1447,6 @@
 		s->cert->pkeys[i].valid_flags = 0;
 		}
 
-	/* TODO(fork): we probably want OCSP stapling support, but this pulls in
-	 * a lot of code. */
-#if 0
-	/* Clear OCSP state. */
-	s->tlsext_status_type = -1;
-	if (s->tlsext_ocsp_ids)
-		{
-		sk_OCSP_RESPID_pop_free(s->tlsext_ocsp_ids, OCSP_RESPID_free);
-		s->tlsext_ocsp_ids = NULL;
-		}
-	if (s->tlsext_ocsp_exts)
-		{
-		sk_X509_EXTENSION_pop_free(s->tlsext_ocsp_exts, X509_EXTENSION_free);
-		s->tlsext_ocsp_exts = NULL;
-		}
-#endif
-
 	/* There may be no extensions. */
 	if (CBS_len(cbs) == 0)
 		{
@@ -1745,119 +1695,6 @@
 				}
 			}
 
-                /* TODO(fork): we probably want OCSP stapling support, but this pulls in a lot of code. */
-#if 0
-		else if (type == TLSEXT_TYPE_status_request)
-			{
-			uint8_t status_type;
-			CBS responder_id_list;
-			CBS request_extensions;
-
-			if (!CBS_get_u8(&extension, &status_type))
-				{
-				*out_alert = SSL_AD_DECODE_ERROR;
-				return 0;
-				}
-
-			/* Only OCSP is supported. */
-			if (status_type != TLSEXT_STATUSTYPE_ocsp)
-				continue;
-
-			s->tlsext_status_type = status_type;
-
-			/* Extension consists of a responder_id_list and
-			 * request_extensions. */
-			if (!CBS_get_u16_length_prefixed(&extension, &responder_id_list) ||
-				!CBS_get_u16_length_prefixed(&extension, &request_extensions) ||
-				CBS_len(&extension) != 0)
-				{
-				*out_alert = SSL_AD_DECODE_ERROR;
-				return 0;
-				}
-
-			if (CBS_len(&responder_id_list) > 0)
-				{
-				s->tlsext_ocsp_ids = sk_OCSP_RESPID_new_null();
-				if (s->tlsext_ocsp_ids == NULL)
-					{
-					*out_alert = SSL_AD_INTERNAL_ERROR;
-					return 0;
-					}
-				}
-
-			/* Parse out the responder IDs. */
-			while (CBS_len(&responder_id_list) > 0)
-				{
-				CBS responder_id;
-				OCSP_RESPID *id;
-				const uint8_t *data;
-
-				/* Each ResponderID must have size at least 1. */
-				if (!CBS_get_u16_length_prefixed(&responder_id_list, &responder_id) ||
-					CBS_len(&responder_id) < 1)
-					{
-					*out_alert = SSL_AD_DECODE_ERROR;
-					return 0;
-					}
-
-				/* TODO(fork): Add CBS versions of d2i_FOO_BAR. */
-				data = CBS_data(&responder_id);
-				id = d2i_OCSP_RESPID(NULL, &data, CBS_len(&responder_id));
-				if (!id)
-					{
-					*out_alert = SSL_AD_DECODE_ERROR;
-					return 0;
-					}
-				if (!CBS_skip(&responder_id, data - CBS_data(&responder_id)))
-					{
-					/* This should never happen. */
-					*out_alert = SSL_AD_INTERNAL_ERROR;
-					OCSP_RESPID_free(id);
-					return 0;
-					}
-				if (CBS_len(&responder_id) != 0)
-					{
-					*out_alert = SSL_AD_DECODE_ERROR;
-					OCSP_RESPID_free(id);
-					return 0;
-					}
-
-				if (!sk_OCSP_RESPID_push(s->tlsext_ocsp_ids, id))
-					{
-					*out_alert = SSL_AD_INTERNAL_ERROR;
-					OCSP_RESPID_free(id);
-					return 0;
-					}
-				}
-
-			/* Parse out request_extensions. */
-			if (CBS_len(&request_extensions) > 0)
-				{
-				const uint8_t *data;
-
-				data = CBS_data(&request_extensions);
-				s->tlsext_ocsp_exts = d2i_X509_EXTENSIONS(NULL,
-					&data, CBS_len(&request_extensions));
-				if (s->tlsext_ocsp_exts == NULL)
-					{
-					*out_alert = SSL_AD_DECODE_ERROR;
-					return 0;
-					}
-				if (!CBS_skip(&request_extensions, data - CBS_data(&request_extensions)))
-					{
-					/* This should never happen. */
-					*out_alert = SSL_AD_INTERNAL_ERROR;
-					return 0;
-					}
-				if (CBS_len(&request_extensions) != 0)
-					{
-					*out_alert = SSL_AD_DECODE_ERROR;
-					return 0;
-					}
-				}
-			}
-#endif
-
 		else if (type == TLSEXT_TYPE_next_proto_neg &&
 			 s->s3->tmp.finish_md_len == 0 &&
 			 s->s3->alpn_selected == NULL)
@@ -1960,7 +1797,7 @@
 		return 0;
 		}
 
-	if (ssl_check_clienthello_tlsext_early(s) <= 0) 
+	if (ssl_check_clienthello_tlsext(s) <= 0)
 		{
 		OPENSSL_PUT_ERROR(SSL, ssl_parse_clienthello_tlsext, SSL_R_CLIENTHELLO_TLSEXT);
 		return 0;
@@ -1993,9 +1830,13 @@
 	int renegotiate_seen = 0;
 	CBS extensions;
 
+	/* TODO(davidben): Move all of these to some per-handshake state that
+	 * gets systematically reset on a new handshake; perhaps allocate it
+	 * fresh each time so it's not even kept around post-handshake. */
 	s->s3->next_proto_neg_seen = 0;
 
-        s->tlsext_ticket_expected = 0;
+	s->tlsext_ticket_expected = 0;
+	s->s3->tmp.certificate_status_expected = 0;
 
 	if (s->s3->alpn_selected)
 		{
@@ -2101,13 +1942,13 @@
 				*out_alert = SSL_AD_DECODE_ERROR;
 				return 0;
 				}
-			if (s->tlsext_status_type == -1)
+			if (!s->ocsp_stapling_enabled)
 				{
 				*out_alert = SSL_AD_UNSUPPORTED_EXTENSION;
 				return 0;
 				}
 			/* Set a flag to expect a CertificateStatus message */
-			s->tlsext_status_expected = 1;
+			s->s3->tmp.certificate_status_expected = 1;
 			}
 		else if (type == TLSEXT_TYPE_next_proto_neg && s->s3->tmp.finish_md_len == 0) {
 		unsigned char *selected;
@@ -2281,7 +2122,7 @@
 	return 1;
 	}
 
-static int ssl_check_clienthello_tlsext_early(SSL *s)
+static int ssl_check_clienthello_tlsext(SSL *s)
 	{
 	int ret=SSL_TLSEXT_ERR_NOACK;
 	int al = SSL_AD_UNRECOGNIZED_NAME;
@@ -2315,72 +2156,7 @@
 		}
 	}
 
-int ssl_check_clienthello_tlsext_late(SSL *s)
-	{
-	int ret = SSL_TLSEXT_ERR_OK;
-	int al;
-
-	/* If status request then ask callback what to do.
- 	 * Note: this must be called after servername callbacks in case
- 	 * the certificate has changed, and must be called after the cipher
-	 * has been chosen because this may influence which certificate is sent
- 	 */
-	if ((s->tlsext_status_type != -1) && s->ctx && s->ctx->tlsext_status_cb)
-		{
-		int r;
-		CERT_PKEY *certpkey;
-		certpkey = ssl_get_server_send_pkey(s);
-		/* If no certificate can't return certificate status */
-		if (certpkey == NULL)
-			{
-			s->tlsext_status_expected = 0;
-			return 1;
-			}
-		/* Set current certificate to one we will use so
-		 * SSL_get_certificate et al can pick it up.
-		 */
-		s->cert->key = certpkey;
-		r = s->ctx->tlsext_status_cb(s, s->ctx->tlsext_status_arg);
-		switch (r)
-			{
-			/* We don't want to send a status request response */
-			case SSL_TLSEXT_ERR_NOACK:
-				s->tlsext_status_expected = 0;
-				break;
-			/* status request response should be sent */
-			case SSL_TLSEXT_ERR_OK:
-				if (s->tlsext_ocsp_resp)
-					s->tlsext_status_expected = 1;
-				else
-					s->tlsext_status_expected = 0;
-				break;
-			/* something bad happened */
-			case SSL_TLSEXT_ERR_ALERT_FATAL:
-				ret = SSL_TLSEXT_ERR_ALERT_FATAL;
-				al = SSL_AD_INTERNAL_ERROR;
-				goto err;
-			}
-		}
-	else
-		s->tlsext_status_expected = 0;
-
- err:
-	switch (ret)
-		{
-		case SSL_TLSEXT_ERR_ALERT_FATAL:
-			ssl3_send_alert(s, SSL3_AL_FATAL, al);
-			return -1;
-
-		case SSL_TLSEXT_ERR_ALERT_WARNING:
-			ssl3_send_alert(s, SSL3_AL_WARNING, al);
-			return 1; 
-
-		default:
-			return 1;
-		}
-	}
-
-int ssl_check_serverhello_tlsext(SSL *s)
+static int ssl_check_serverhello_tlsext(SSL *s)
 	{
 	int ret=SSL_TLSEXT_ERR_NOACK;
 	int al = SSL_AD_UNRECOGNIZED_NAME;
@@ -2421,35 +2197,6 @@
 	else if (s->initial_ctx != NULL && s->initial_ctx->tlsext_servername_callback != 0) 		
 		ret = s->initial_ctx->tlsext_servername_callback(s, &al, s->initial_ctx->tlsext_servername_arg);
 
-	/* If we've requested certificate status and we wont get one
- 	 * tell the callback
- 	 */
-	if ((s->tlsext_status_type != -1) && !(s->tlsext_status_expected)
-			&& s->ctx && s->ctx->tlsext_status_cb)
-		{
-		int r;
-		/* Set resp to NULL, resplen to -1 so callback knows
- 		 * there is no response.
- 		 */
-		if (s->tlsext_ocsp_resp)
-			{
-			OPENSSL_free(s->tlsext_ocsp_resp);
-			s->tlsext_ocsp_resp = NULL;
-			}
-		s->tlsext_ocsp_resplen = -1;
-		r = s->ctx->tlsext_status_cb(s, s->ctx->tlsext_status_arg);
-		if (r == 0)
-			{
-			al = SSL_AD_BAD_CERTIFICATE_STATUS_RESPONSE;
-			ret = SSL_TLSEXT_ERR_ALERT_FATAL;
-			}
-		if (r < 0)
-			{
-			al = SSL_AD_INTERNAL_ERROR;
-			ret = SSL_TLSEXT_ERR_ALERT_FATAL;
-			}
-		}
-
 	switch (ret)
 		{
 		case SSL_TLSEXT_ERR_ALERT_FATAL: