Server-side OCSP stapling support.

This is a simpler implementation than OpenSSL's, lacking responder IDs
and request extensions support. This mirrors the client implementation
already present.

Change-Id: I54592b60e0a708bfb003d491c9250401403c9e69
Reviewed-on: https://boringssl-review.googlesource.com/5700
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/ssl/d1_srvr.c b/ssl/d1_srvr.c
index 3dd7701..ca08651 100644
--- a/ssl/d1_srvr.c
+++ b/ssl/d1_srvr.c
@@ -239,6 +239,16 @@
         s->init_num = 0;
         break;
 
+      case SSL3_ST_SW_CERT_STATUS_A:
+      case SSL3_ST_SW_CERT_STATUS_B:
+        ret = ssl3_send_certificate_status(s);
+        if (ret <= 0) {
+          goto end;
+        }
+        s->state = SSL3_ST_SW_KEY_EXCH_A;
+        s->init_num = 0;
+        break;
+
       case SSL3_ST_SW_KEY_EXCH_A:
       case SSL3_ST_SW_KEY_EXCH_B:
       case SSL3_ST_SW_KEY_EXCH_C:
diff --git a/ssl/internal.h b/ssl/internal.h
index 40ca00b..6781e26 100644
--- a/ssl/internal.h
+++ b/ssl/internal.h
@@ -880,7 +880,7 @@
 
 int ssl3_send_server_certificate(SSL *s);
 int ssl3_send_new_session_ticket(SSL *s);
-int ssl3_send_cert_status(SSL *s);
+int ssl3_send_certificate_status(SSL *s);
 int ssl3_get_finished(SSL *s, int state_a, int state_b);
 int ssl3_send_change_cipher_spec(SSL *s, int state_a, int state_b);
 int ssl3_prf(SSL *s, uint8_t *out, size_t out_len, const uint8_t *secret,
diff --git a/ssl/s3_srvr.c b/ssl/s3_srvr.c
index 8045260..5a46abc 100644
--- a/ssl/s3_srvr.c
+++ b/ssl/s3_srvr.c
@@ -306,6 +306,16 @@
         s->init_num = 0;
         break;
 
+      case SSL3_ST_SW_CERT_STATUS_A:
+      case SSL3_ST_SW_CERT_STATUS_B:
+        ret = ssl3_send_certificate_status(s);
+        if (ret <= 0) {
+          goto end;
+        }
+        s->state = SSL3_ST_SW_KEY_EXCH_A;
+        s->init_num = 0;
+        break;
+
       case SSL3_ST_SW_KEY_EXCH_A:
       case SSL3_ST_SW_KEY_EXCH_B:
       case SSL3_ST_SW_KEY_EXCH_C:
@@ -1191,6 +1201,32 @@
   return ssl_do_write(s);
 }
 
+int ssl3_send_certificate_status(SSL *ssl) {
+  if (ssl->state == SSL3_ST_SW_CERT_STATUS_A) {
+    CBB out, ocsp_response;
+    size_t length;
+
+    CBB_zero(&out);
+    if (!CBB_init_fixed(&out, ssl_handshake_start(ssl),
+                        ssl->init_buf->max - SSL_HM_HEADER_LENGTH(ssl)) ||
+        !CBB_add_u8(&out, TLSEXT_STATUSTYPE_ocsp) ||
+        !CBB_add_u24_length_prefixed(&out, &ocsp_response) ||
+        !CBB_add_bytes(&ocsp_response, ssl->ctx->ocsp_response,
+                       ssl->ctx->ocsp_response_length) ||
+        !CBB_finish(&out, NULL, &length) ||
+        !ssl_set_handshake_header(ssl, SSL3_MT_CERTIFICATE_STATUS, length)) {
+      OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+      CBB_cleanup(&out);
+      return -1;
+    }
+
+    ssl->state = SSL3_ST_SW_CERT_STATUS_B;
+  }
+
+  /* SSL3_ST_SW_CERT_STATUS_B */
+  return ssl_do_write(ssl);
+}
+
 int ssl3_send_server_done(SSL *s) {
   if (s->state == SSL3_ST_SW_SRVR_DONE_A) {
     if (!ssl_set_handshake_header(s, SSL3_MT_SERVER_DONE, 0)) {
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index e2437b9..988b3a1 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -1385,6 +1385,20 @@
   *out_len = session->ocsp_response_length;
 }
 
+int SSL_CTX_set_ocsp_response(SSL_CTX *ctx, const uint8_t *response,
+                              size_t response_len) {
+  OPENSSL_free(ctx->ocsp_response);
+  ctx->ocsp_response_length = 0;
+
+  ctx->ocsp_response = BUF_memdup(response, response_len);
+  if (ctx->ocsp_response == NULL) {
+    return 0;
+  }
+  ctx->ocsp_response_length = response_len;
+
+  return 1;
+}
+
 /* 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.
@@ -1753,6 +1767,7 @@
   OPENSSL_free(ctx->psk_identity_hint);
   OPENSSL_free(ctx->tlsext_ellipticcurvelist);
   OPENSSL_free(ctx->alpn_client_proto_list);
+  OPENSSL_free(ctx->ocsp_response);
   EVP_PKEY_free(ctx->tlsext_channel_id_private);
   BIO_free(ctx->keylog_bio);
 
diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c
index 3902f8f..2eeffab 100644
--- a/ssl/t1_lib.c
+++ b/ssl/t1_lib.c
@@ -1330,7 +1330,7 @@
 }
 
 static int ext_ocsp_parse_serverhello(SSL *ssl, uint8_t *out_alert,
-                                         CBS *contents) {
+                                      CBS *contents) {
   if (contents == NULL) {
     return 1;
   }
@@ -1345,13 +1345,32 @@
 
 static int ext_ocsp_parse_clienthello(SSL *ssl, uint8_t *out_alert,
                                       CBS *contents) {
-  /* OCSP stapling as a server is not supported. */
+  if (contents == NULL) {
+    return 1;
+  }
+
+  uint8_t status_type;
+  if (!CBS_get_u8(contents, &status_type)) {
+    return 0;
+  }
+
+  /* We cannot decide whether OCSP stapling will occur yet because the correct
+   * SSL_CTX might not have been selected. */
+  ssl->s3->tmp.ocsp_stapling_requested = status_type == TLSEXT_STATUSTYPE_ocsp;
+
   return 1;
 }
 
 static int ext_ocsp_add_serverhello(SSL *ssl, CBB *out) {
-  /* OCSP stapling as a server is not supported. */
-  return 1;
+  if (!ssl->s3->tmp.ocsp_stapling_requested ||
+      ssl->ctx->ocsp_response_length == 0) {
+    return 1;
+  }
+
+  ssl->s3->tmp.certificate_status_expected = 1;
+
+  return CBB_add_u16(out, TLSEXT_TYPE_status_request) &&
+         CBB_add_u16(out, 0 /* length */);
 }
 
 
diff --git a/ssl/test/bssl_shim.cc b/ssl/test/bssl_shim.cc
index 53f8e29..d839f5f 100644
--- a/ssl/test/bssl_shim.cc
+++ b/ssl/test/bssl_shim.cc
@@ -240,6 +240,12 @@
                                 SSL_FILETYPE_PEM)) {
     return false;
   }
+  if (!config->ocsp_response.empty() &&
+      !SSL_CTX_set_ocsp_response(ssl->ctx,
+                                 (const uint8_t *)config->ocsp_response.data(),
+                                 config->ocsp_response.size())) {
+    return false;
+  }
   return true;
 }
 
diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go
index 49ada2a..b379074 100644
--- a/ssl/test/runner/runner.go
+++ b/ssl/test/runner/runner.go
@@ -155,6 +155,8 @@
 	// expectedSRTPProtectionProfile is the DTLS-SRTP profile that
 	// should be negotiated. If zero, none should be negotiated.
 	expectedSRTPProtectionProfile uint16
+	// expectedOCSPResponse, if not nil, is the expected OCSP response to be received.
+	expectedOCSPResponse []uint8
 	// messageLen is the length, in bytes, of the test message that will be
 	// sent.
 	messageLen int
@@ -320,6 +322,10 @@
 		return fmt.Errorf("SRTP profile mismatch: got %d, wanted %d", p, test.expectedSRTPProtectionProfile)
 	}
 
+	if test.expectedOCSPResponse != nil && !bytes.Equal(test.expectedOCSPResponse, tlsConn.OCSPResponse()) {
+		return fmt.Errorf("OCSP Response mismatch")
+	}
+
 	if test.exportKeyingMaterial > 0 {
 		actual := make([]byte, test.exportKeyingMaterial)
 		if _, err := io.ReadFull(tlsConn, actual); err != nil {
@@ -2333,6 +2339,26 @@
 		flags: []string{"-psk", "secret"},
 	})
 
+	tests = append(tests, testCase{
+		testType: clientTest,
+		name:     "OCSPStapling-Client",
+		flags: []string{
+			"-enable-ocsp-stapling",
+			"-expect-ocsp-response",
+			base64.StdEncoding.EncodeToString(testOCSPResponse),
+		},
+	})
+
+	tests = append(tests, testCase{
+		testType: serverTest,
+		name:     "OCSPStapling-Server",
+		expectedOCSPResponse: testOCSPResponse,
+		flags: []string{
+			"-ocsp-response",
+			base64.StdEncoding.EncodeToString(testOCSPResponse),
+		},
+	})
+
 	if protocol == tls {
 		tests = append(tests, testCase{
 			name:        "Renegotiate-Client",
@@ -3034,15 +3060,7 @@
 		shouldFail:    true,
 		expectedError: ":BAD_SRTP_PROTECTION_PROFILE_LIST:",
 	})
-	// Test OCSP stapling and SCT list.
-	testCases = append(testCases, testCase{
-		name: "OCSPStapling",
-		flags: []string{
-			"-enable-ocsp-stapling",
-			"-expect-ocsp-response",
-			base64.StdEncoding.EncodeToString(testOCSPResponse),
-		},
-	})
+	// Test SCT list.
 	testCases = append(testCases, testCase{
 		name: "SignedCertificateTimestampList",
 		flags: []string{
diff --git a/ssl/test/test_config.cc b/ssl/test/test_config.cc
index d873d8e..8c4b420 100644
--- a/ssl/test/test_config.cc
+++ b/ssl/test/test_config.cc
@@ -119,6 +119,7 @@
   { "-expect-ocsp-response", &TestConfig::expected_ocsp_response },
   { "-expect-signed-cert-timestamps",
     &TestConfig::expected_signed_cert_timestamps },
+  { "-ocsp-response", &TestConfig::ocsp_response },
 };
 
 const Flag<int> kIntFlags[] = {
diff --git a/ssl/test/test_config.h b/ssl/test/test_config.h
index 29a1c77..4418ed3 100644
--- a/ssl/test/test_config.h
+++ b/ssl/test/test_config.h
@@ -86,6 +86,7 @@
   bool enable_server_custom_extension = false;
   bool custom_extension_skip = false;
   bool custom_extension_fail_add = false;
+  std::string ocsp_response;
 };
 
 bool ParseConfig(int argc, char **argv, TestConfig *out_config);