From 81d4909d00c3628453a8712bc331304bd01d8eaf Mon Sep 17 00:00:00 2001 From: David Benjamin Date: Thu, 10 May 2018 19:55:02 -0400 Subject: [PATCH] Implement legacy OCSP APIs for libssl. Previously, we'd omitted OpenSSL's OCSP APIs because they depend on a complex OCSP mechanism and encourage the the unreliable server behavior that hampers using OCSP stapling to fix revocation today. (OCSP responses should not be fetched on-demand on a callback. They should be managed like other server credentials and refreshed eagerly, so temporary CA outage does not translate to loss of OCSP.) But most of the APIs are byte-oriented anyway, so they're easy to support. Intentionally omit the one that takes a bunch of OCSP_RESPIDs. The callback is benign on the client (an artifact of OpenSSL reading OCSP and verifying certificates in the wrong order). On the server, it encourages unreliability, but pyOpenSSL/cryptography.io depends on this. Dcument that this is only for compatibility with legacy software. Also tweak a few things for compatilibility. cryptography.io expects SSL_CTX_set_read_ahead to return something, SSL_get_server_tmp_key's signature was wrong, and cryptography.io tries to redefine SSL_get_server_tmp_key if SSL_CTRL_GET_SERVER_TMP_KEY is missing. Change-Id: I2f99711783456bfb7324e9ad972510be8a95e845 Reviewed-on: https://boringssl-review.googlesource.com/28404 Commit-Queue: David Benjamin CQ-Verified: CQ bot account: commit-bot@chromium.org Reviewed-by: Adam Langley --- crypto/err/ssl.errordata | 1 + include/openssl/ssl.h | 64 +++++++++++++-- include/openssl/tls1.h | 1 + ssl/handshake.cc | 16 ++++ ssl/handshake_server.cc | 16 ++++ ssl/internal.h | 5 ++ ssl/ssl_lib.cc | 39 ++++++++- ssl/test/bssl_shim.cc | 26 ++++++ ssl/test/runner/alert.go | 122 +++++++++++++-------------- ssl/test/runner/runner.go | 205 ++++++++++++++++++++++++++++++++++------------ ssl/test/test_config.cc | 4 + ssl/test/test_config.h | 4 + 12 files changed, 381 insertions(+), 122 deletions(-) diff --git a/crypto/err/ssl.errordata b/crypto/err/ssl.errordata index 7b63bc8..375df9a 100644 --- a/crypto/err/ssl.errordata +++ b/crypto/err/ssl.errordata @@ -108,6 +108,7 @@ SSL,266,NO_SHARED_GROUP SSL,280,NO_SUPPORTED_VERSIONS_ENABLED SSL,185,NULL_SSL_CTX SSL,186,NULL_SSL_METHOD_PASSED +SSL,289,OCSP_CB_ERROR SSL,187,OLD_SESSION_CIPHER_NOT_RETURNED SSL,268,OLD_SESSION_PRF_HASH_MISMATCH SSL,188,OLD_SESSION_VERSION_NOT_RETURNED diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h index 35506f7..d46a5af 100644 --- a/include/openssl/ssl.h +++ b/include/openssl/ssl.h @@ -3715,14 +3715,14 @@ OPENSSL_EXPORT int SSL_set_tmp_rsa(SSL *ssl, const RSA *rsa); // SSL_CTX_get_read_ahead returns zero. OPENSSL_EXPORT int SSL_CTX_get_read_ahead(const SSL_CTX *ctx); -// SSL_CTX_set_read_ahead does nothing. -OPENSSL_EXPORT void SSL_CTX_set_read_ahead(SSL_CTX *ctx, int yes); +// SSL_CTX_set_read_ahead returns one. +OPENSSL_EXPORT int SSL_CTX_set_read_ahead(SSL_CTX *ctx, int yes); // SSL_get_read_ahead returns zero. OPENSSL_EXPORT int SSL_get_read_ahead(const SSL *ssl); -// SSL_set_read_ahead does nothing. -OPENSSL_EXPORT void SSL_set_read_ahead(SSL *ssl, int yes); +// SSL_set_read_ahead returns one. +OPENSSL_EXPORT int SSL_set_read_ahead(SSL *ssl, int yes); // SSL_renegotiate put an error on the error queue and returns zero. OPENSSL_EXPORT int SSL_renegotiate(SSL *ssl); @@ -3793,7 +3793,7 @@ OPENSSL_EXPORT const COMP_METHOD *SSL_get_current_compression(SSL *ssl); OPENSSL_EXPORT const COMP_METHOD *SSL_get_current_expansion(SSL *ssl); // SSL_get_server_tmp_key returns zero. -OPENSSL_EXPORT int *SSL_get_server_tmp_key(SSL *ssl, EVP_PKEY **out_key); +OPENSSL_EXPORT int SSL_get_server_tmp_key(SSL *ssl, EVP_PKEY **out_key); // SSL_CTX_set_tmp_dh returns 1. OPENSSL_EXPORT int SSL_CTX_set_tmp_dh(SSL_CTX *ctx, const DH *dh); @@ -4108,6 +4108,58 @@ extern "C++" OPENSSL_EXPORT void SSL_CTX_sess_set_get_cb( int id_len, int *out_copy)); #endif +// SSL_set_tlsext_status_type configures a client to request OCSP stapling if +// |type| is |TLSEXT_STATUSTYPE_ocsp| and disables it otherwise. It returns one +// on success and zero if handshake configuration has already been shed. +// +// Use |SSL_enable_ocsp_stapling| instead. +OPENSSL_EXPORT int SSL_set_tlsext_status_type(SSL *ssl, int type); + +// SSL_set_tlsext_status_ocsp_resp sets the OCSP response. It returns one on +// success and zero on error. On success, |ssl| takes ownership of |resp|, which +// must have been allocated by |OPENSSL_malloc|. +// +// Use |SSL_set_ocsp_response| instead. +OPENSSL_EXPORT int SSL_set_tlsext_status_ocsp_resp(SSL *ssl, uint8_t *resp, + size_t resp_len); + +// SSL_get_tlsext_status_ocsp_resp sets |*out| to point to the OCSP response +// from the server. It returns the length of the response. If there was no +// response, it sets |*out| to NULL and returns zero. +// +// Use |SSL_get0_ocsp_response| instead. +// +// WARNING: the returned data is not guaranteed to be well formed. +OPENSSL_EXPORT size_t SSL_get_tlsext_status_ocsp_resp(const SSL *ssl, + const uint8_t **out); + +// SSL_CTX_set_tlsext_status_cb configures the legacy OpenSSL OCSP callback and +// returns one. Though the type signature is the same, this callback has +// different behavior for client and server connections: +// +// For clients, the callback is called after certificate verification. It should +// return one for success, zero for a bad OCSP response, and a negative number +// for internal error. Instead, handle this as part of certificate verification. +// (Historically, OpenSSL verified certificates just before parsing stapled OCSP +// responses, but BoringSSL fixes this ordering. All server credentials are +// available during verification.) +// +// Do not use this callback as a server. It is provided for compatibility +// purposes only. For servers, it is called to configure server credentials. It +// should return |SSL_TLSEXT_ERR_OK| on success, |SSL_TLSEXT_ERR_NOACK| to +// ignore OCSP requests, or |SSL_TLSEXT_ERR_ALERT_FATAL| on error. It is usually +// used to fetch OCSP responses on demand, which is not ideal. Instead, treat +// OCSP responses like other server credentials, such as certificates or SCT +// lists. Configure, store, and refresh them eagerly. This avoids downtime if +// the CA's OCSP responder is briefly offline. +OPENSSL_EXPORT int SSL_CTX_set_tlsext_status_cb(SSL_CTX *ctx, + int (*callback)(SSL *ssl, + void *arg)); + +// SSL_CTX_set_tlsext_status_arg sets additional data for +// |SSL_CTX_set_tlsext_status_cb|'s callback and returns one. +OPENSSL_EXPORT int SSL_CTX_set_tlsext_status_arg(SSL_CTX *ctx, void *arg); + // Private structures. // @@ -4285,6 +4337,7 @@ struct ssl_session_st { #define SSL_CTRL_GET_NUM_RENEGOTIATIONS doesnt_exist #define SSL_CTRL_GET_READ_AHEAD doesnt_exist #define SSL_CTRL_GET_RI_SUPPORT doesnt_exist +#define SSL_CTRL_GET_SERVER_TMP_KEY doesnt_exist #define SSL_CTRL_GET_SESSION_REUSED doesnt_exist #define SSL_CTRL_GET_SESS_CACHE_MODE doesnt_exist #define SSL_CTRL_GET_SESS_CACHE_SIZE doesnt_exist @@ -4698,6 +4751,7 @@ OPENSSL_EXPORT bool SSL_apply_handback(SSL *ssl, Span handback); #define SSL_R_NEGOTIATED_TB_WITHOUT_EMS_OR_RI 285 #define SSL_R_SERVER_ECHOED_INVALID_SESSION_ID 286 #define SSL_R_PRIVATE_KEY_OPERATION_FAILED 287 +#define SSL_R_OCSP_CB_ERROR 289 #define SSL_R_SSLV3_ALERT_CLOSE_NOTIFY 1000 #define SSL_R_SSLV3_ALERT_UNEXPECTED_MESSAGE 1010 #define SSL_R_SSLV3_ALERT_BAD_RECORD_MAC 1020 diff --git a/include/openssl/tls1.h b/include/openssl/tls1.h index 3424f3d..7a05969 100644 --- a/include/openssl/tls1.h +++ b/include/openssl/tls1.h @@ -237,6 +237,7 @@ extern "C" { #define TLSEXT_TYPE_dummy_pq_padding 54537 // status request value from RFC 3546 +#define TLSEXT_STATUSTYPE_nothing (-1) #define TLSEXT_STATUSTYPE_ocsp 1 // ECPointFormat values from RFC 4492 diff --git a/ssl/handshake.cc b/ssl/handshake.cc index 6432424..0a90b9f 100644 --- a/ssl/handshake.cc +++ b/ssl/handshake.cc @@ -356,6 +356,22 @@ enum ssl_verify_result_t ssl_verify_peer_cert(SSL_HANDSHAKE *hs) { ssl_send_alert(ssl, SSL3_AL_FATAL, alert); } + // Emulate OpenSSL's client OCSP callback. OpenSSL verifies certificates + // before it receives the OCSP, so it needs a second callback for OCSP. + if (ret == ssl_verify_ok && !ssl->server && + hs->new_session->ocsp_response != nullptr && + ssl->ctx->legacy_ocsp_callback != nullptr) { + int cb_ret = + ssl->ctx->legacy_ocsp_callback(ssl, ssl->ctx->legacy_ocsp_callback_arg); + if (cb_ret <= 0) { + OPENSSL_PUT_ERROR(SSL, SSL_R_OCSP_CB_ERROR); + ssl_send_alert(ssl, SSL3_AL_FATAL, + cb_ret == 0 ? SSL_AD_BAD_CERTIFICATE_STATUS_RESPONSE + : SSL_AD_INTERNAL_ERROR); + ret = ssl_verify_invalid; + } + } + return ret; } diff --git a/ssl/handshake_server.cc b/ssl/handshake_server.cc index fa8a241..7a96767 100644 --- a/ssl/handshake_server.cc +++ b/ssl/handshake_server.cc @@ -534,6 +534,22 @@ static enum ssl_hs_wait_t do_select_certificate(SSL_HANDSHAKE *hs) { return ssl_hs_error; } + if (hs->ocsp_stapling_requested && + ssl->ctx->legacy_ocsp_callback != nullptr) { + switch (ssl->ctx->legacy_ocsp_callback( + ssl, ssl->ctx->legacy_ocsp_callback_arg)) { + case SSL_TLSEXT_ERR_OK: + break; + case SSL_TLSEXT_ERR_NOACK: + hs->ocsp_stapling_requested = false; + break; + default: + OPENSSL_PUT_ERROR(SSL, SSL_R_OCSP_CB_ERROR); + ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR); + return ssl_hs_error; + } + } + if (ssl_protocol_version(ssl) >= TLS1_3_VERSION) { // Jump to the TLS 1.3 state machine. hs->state = state_tls13; diff --git a/ssl/internal.h b/ssl/internal.h index d13d5f2..1cdfb8e 100644 --- a/ssl/internal.h +++ b/ssl/internal.h @@ -2140,6 +2140,11 @@ struct SSLContext { // session tickets. const SSL_TICKET_AEAD_METHOD *ticket_aead_method; + // legacy_ocsp_callback implements an OCSP-related callback for OpenSSL + // compatibility. + int (*legacy_ocsp_callback)(SSL *ssl, void *arg); + void *legacy_ocsp_callback_arg; + // verify_sigalgs, if not empty, is the set of signature algorithms // accepted from the peer in decreasing order of preference. uint16_t *verify_sigalgs; diff --git a/ssl/ssl_lib.cc b/ssl/ssl_lib.cc index 9f56d54..50608e9 100644 --- a/ssl/ssl_lib.cc +++ b/ssl/ssl_lib.cc @@ -1591,9 +1591,9 @@ int SSL_CTX_get_read_ahead(const SSL_CTX *ctx) { return 0; } int SSL_get_read_ahead(const SSL *ssl) { return 0; } -void SSL_CTX_set_read_ahead(SSL_CTX *ctx, int yes) { } +int SSL_CTX_set_read_ahead(SSL_CTX *ctx, int yes) { return 1; } -void SSL_set_read_ahead(SSL *ssl, int yes) { } +int SSL_set_read_ahead(SSL *ssl, int yes) { return 1; } int SSL_pending(const SSL *ssl) { return static_cast(ssl->s3->pending_app_data.size()); @@ -2205,7 +2205,7 @@ const COMP_METHOD *SSL_get_current_compression(SSL *ssl) { return NULL; } const COMP_METHOD *SSL_get_current_expansion(SSL *ssl) { return NULL; } -int *SSL_get_server_tmp_key(SSL *ssl, EVP_PKEY **out_key) { return 0; } +int SSL_get_server_tmp_key(SSL *ssl, EVP_PKEY **out_key) { return 0; } void SSL_CTX_set_quiet_shutdown(SSL_CTX *ctx, int mode) { ctx->quiet_shutdown = (mode != 0); @@ -2717,3 +2717,33 @@ void SSL_CTX_set_ticket_aead_method(SSL_CTX *ctx, const SSL_TICKET_AEAD_METHOD *aead_method) { ctx->ticket_aead_method = aead_method; } + +int SSL_set_tlsext_status_type(SSL *ssl, int type) { + ssl->ocsp_stapling_enabled = type == TLSEXT_STATUSTYPE_ocsp; + return 1; +} + +int SSL_set_tlsext_status_ocsp_resp(SSL *ssl, uint8_t *resp, size_t resp_len) { + if (SSL_set_ocsp_response(ssl, resp, resp_len)) { + OPENSSL_free(resp); + return 1; + } + return 0; +} + +size_t SSL_get_tlsext_status_ocsp_resp(const SSL *ssl, const uint8_t **out) { + size_t ret; + SSL_get0_ocsp_response(ssl, out, &ret); + return ret; +} + +int SSL_CTX_set_tlsext_status_cb(SSL_CTX *ctx, + int (*callback)(SSL *ssl, void *arg)) { + ctx->legacy_ocsp_callback = callback; + return 1; +} + +int SSL_CTX_set_tlsext_status_arg(SSL_CTX *ctx, void *arg) { + ctx->legacy_ocsp_callback_arg = arg; + return 1; +} diff --git a/ssl/test/bssl_shim.cc b/ssl/test/bssl_shim.cc index ae26ded..3a33d60 100644 --- a/ssl/test/bssl_shim.cc +++ b/ssl/test/bssl_shim.cc @@ -495,6 +495,7 @@ static bool GetCertificate(SSL *ssl, bssl::UniquePtr *out_x509, return false; } if (!config->ocsp_response.empty() && + !config->set_ocsp_in_callback && !SSL_set_ocsp_response(ssl, (const uint8_t *)config->ocsp_response.data(), config->ocsp_response.size())) { return false; @@ -1100,6 +1101,27 @@ static void MessageCallback(int is_write, int version, int content_type, } } +static int LegacyOCSPCallback(SSL *ssl, void *arg) { + const TestConfig *config = GetTestConfig(ssl); + if (!SSL_is_server(ssl)) { + return !config->fail_ocsp_callback; + } + + if (!config->ocsp_response.empty() && + config->set_ocsp_in_callback && + !SSL_set_ocsp_response(ssl, (const uint8_t *)config->ocsp_response.data(), + config->ocsp_response.size())) { + return SSL_TLSEXT_ERR_ALERT_FATAL; + } + if (config->fail_ocsp_callback) { + return SSL_TLSEXT_ERR_ALERT_FATAL; + } + if (config->decline_ocsp_callback) { + return SSL_TLSEXT_ERR_NOACK; + } + return SSL_TLSEXT_ERR_OK; +} + // Connect returns a new socket connected to localhost on |port| or -1 on // error. static int Connect(uint16_t port) { @@ -1334,6 +1356,10 @@ static bssl::UniquePtr SetupCtx(SSL_CTX *old_ctx, SSL_CTX_set_false_start_allowed_without_alpn(ssl_ctx.get(), 1); } + if (config->use_ocsp_callback) { + SSL_CTX_set_tlsext_status_cb(ssl_ctx.get(), LegacyOCSPCallback); + } + if (old_ctx) { uint8_t keys[48]; if (!SSL_CTX_get_tlsext_ticket_keys(old_ctx, &keys, sizeof(keys)) || diff --git a/ssl/test/runner/alert.go b/ssl/test/runner/alert.go index 652e9ee..c79725e 100644 --- a/ssl/test/runner/alert.go +++ b/ssl/test/runner/alert.go @@ -15,69 +15,71 @@ const ( ) const ( - alertCloseNotify alert = 0 - alertEndOfEarlyData alert = 1 - alertUnexpectedMessage alert = 10 - alertBadRecordMAC alert = 20 - alertDecryptionFailed alert = 21 - alertRecordOverflow alert = 22 - alertDecompressionFailure alert = 30 - alertHandshakeFailure alert = 40 - alertNoCertificate alert = 41 - alertBadCertificate alert = 42 - alertUnsupportedCertificate alert = 43 - alertCertificateRevoked alert = 44 - alertCertificateExpired alert = 45 - alertCertificateUnknown alert = 46 - alertIllegalParameter alert = 47 - alertUnknownCA alert = 48 - alertAccessDenied alert = 49 - alertDecodeError alert = 50 - alertDecryptError alert = 51 - alertProtocolVersion alert = 70 - alertInsufficientSecurity alert = 71 - alertInternalError alert = 80 - alertInappropriateFallback alert = 86 - alertUserCanceled alert = 90 - alertNoRenegotiation alert = 100 - alertMissingExtension alert = 109 - alertUnsupportedExtension alert = 110 - alertUnrecognizedName alert = 112 - alertUnknownPSKIdentity alert = 115 - alertCertificateRequired alert = 116 + alertCloseNotify alert = 0 + alertEndOfEarlyData alert = 1 + alertUnexpectedMessage alert = 10 + alertBadRecordMAC alert = 20 + alertDecryptionFailed alert = 21 + alertRecordOverflow alert = 22 + alertDecompressionFailure alert = 30 + alertHandshakeFailure alert = 40 + alertNoCertificate alert = 41 + alertBadCertificate alert = 42 + alertUnsupportedCertificate alert = 43 + alertCertificateRevoked alert = 44 + alertCertificateExpired alert = 45 + alertCertificateUnknown alert = 46 + alertIllegalParameter alert = 47 + alertUnknownCA alert = 48 + alertAccessDenied alert = 49 + alertDecodeError alert = 50 + alertDecryptError alert = 51 + alertProtocolVersion alert = 70 + alertInsufficientSecurity alert = 71 + alertInternalError alert = 80 + alertInappropriateFallback alert = 86 + alertUserCanceled alert = 90 + alertNoRenegotiation alert = 100 + alertMissingExtension alert = 109 + alertUnsupportedExtension alert = 110 + alertUnrecognizedName alert = 112 + alertBadCertificateStatusResponse alert = 113 + alertUnknownPSKIdentity alert = 115 + alertCertificateRequired alert = 116 ) var alertText = map[alert]string{ - alertCloseNotify: "close notify", - alertEndOfEarlyData: "end of early data", - alertUnexpectedMessage: "unexpected message", - alertBadRecordMAC: "bad record MAC", - alertDecryptionFailed: "decryption failed", - alertRecordOverflow: "record overflow", - alertDecompressionFailure: "decompression failure", - alertHandshakeFailure: "handshake failure", - alertNoCertificate: "no certificate", - alertBadCertificate: "bad certificate", - alertUnsupportedCertificate: "unsupported certificate", - alertCertificateRevoked: "revoked certificate", - alertCertificateExpired: "expired certificate", - alertCertificateUnknown: "unknown certificate", - alertIllegalParameter: "illegal parameter", - alertUnknownCA: "unknown certificate authority", - alertAccessDenied: "access denied", - alertDecodeError: "error decoding message", - alertDecryptError: "error decrypting message", - alertProtocolVersion: "protocol version not supported", - alertInsufficientSecurity: "insufficient security level", - alertInternalError: "internal error", - alertInappropriateFallback: "inappropriate fallback", - alertUserCanceled: "user canceled", - alertNoRenegotiation: "no renegotiation", - alertMissingExtension: "missing extension", - alertUnsupportedExtension: "unsupported extension", - alertUnrecognizedName: "unrecognized name", - alertUnknownPSKIdentity: "unknown PSK identity", - alertCertificateRequired: "certificate required", + alertCloseNotify: "close notify", + alertEndOfEarlyData: "end of early data", + alertUnexpectedMessage: "unexpected message", + alertBadRecordMAC: "bad record MAC", + alertDecryptionFailed: "decryption failed", + alertRecordOverflow: "record overflow", + alertDecompressionFailure: "decompression failure", + alertHandshakeFailure: "handshake failure", + alertNoCertificate: "no certificate", + alertBadCertificate: "bad certificate", + alertUnsupportedCertificate: "unsupported certificate", + alertCertificateRevoked: "revoked certificate", + alertCertificateExpired: "expired certificate", + alertCertificateUnknown: "unknown certificate", + alertIllegalParameter: "illegal parameter", + alertUnknownCA: "unknown certificate authority", + alertAccessDenied: "access denied", + alertDecodeError: "error decoding message", + alertDecryptError: "error decrypting message", + alertProtocolVersion: "protocol version not supported", + alertInsufficientSecurity: "insufficient security level", + alertInternalError: "internal error", + alertInappropriateFallback: "inappropriate fallback", + alertUserCanceled: "user canceled", + alertNoRenegotiation: "no renegotiation", + alertMissingExtension: "missing extension", + alertUnsupportedExtension: "unsupported extension", + alertBadCertificateStatusResponse: "bad certificate status response", + alertUnrecognizedName: "unrecognized name", + alertUnknownPSKIdentity: "unknown PSK identity", + alertCertificateRequired: "certificate required", } func (e alert) String() string { diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go index 510a48b..1a6d0f9 100644 --- a/ssl/test/runner/runner.go +++ b/ssl/test/runner/runner.go @@ -4744,60 +4744,157 @@ func addStateMachineCoverageTests(config stateMachineTestConfig) { }) // OCSP stapling tests. - tests = append(tests, testCase{ - testType: clientTest, - name: "OCSPStapling-Client", - config: Config{ - MaxVersion: VersionTLS12, - }, - flags: []string{ - "-enable-ocsp-stapling", - "-expect-ocsp-response", - base64.StdEncoding.EncodeToString(testOCSPResponse), - "-verify-peer", - }, - resumeSession: true, - }) - tests = append(tests, testCase{ - testType: serverTest, - name: "OCSPStapling-Server", - config: Config{ - MaxVersion: VersionTLS12, - }, - expectedOCSPResponse: testOCSPResponse, - flags: []string{ - "-ocsp-response", - base64.StdEncoding.EncodeToString(testOCSPResponse), - }, - resumeSession: true, - }) - tests = append(tests, testCase{ - testType: clientTest, - name: "OCSPStapling-Client-TLS13", - config: Config{ - MaxVersion: VersionTLS13, - }, - flags: []string{ - "-enable-ocsp-stapling", - "-expect-ocsp-response", - base64.StdEncoding.EncodeToString(testOCSPResponse), - "-verify-peer", - }, - resumeSession: true, - }) - tests = append(tests, testCase{ - testType: serverTest, - name: "OCSPStapling-Server-TLS13", - config: Config{ - MaxVersion: VersionTLS13, - }, - expectedOCSPResponse: testOCSPResponse, - flags: []string{ - "-ocsp-response", - base64.StdEncoding.EncodeToString(testOCSPResponse), - }, - resumeSession: true, - }) + for _, vers := range tlsVersions { + if config.protocol == dtls && !vers.hasDTLS { + continue + } + if vers.version == VersionSSL30 { + continue + } + tests = append(tests, testCase{ + testType: clientTest, + name: "OCSPStapling-Client-" + vers.name, + config: Config{ + MaxVersion: vers.version, + }, + tls13Variant: vers.tls13Variant, + flags: []string{ + "-enable-ocsp-stapling", + "-expect-ocsp-response", + base64.StdEncoding.EncodeToString(testOCSPResponse), + "-verify-peer", + }, + resumeSession: true, + }) + tests = append(tests, testCase{ + testType: serverTest, + name: "OCSPStapling-Server-" + vers.name, + config: Config{ + MaxVersion: vers.version, + }, + tls13Variant: vers.tls13Variant, + expectedOCSPResponse: testOCSPResponse, + flags: []string{ + "-ocsp-response", + base64.StdEncoding.EncodeToString(testOCSPResponse), + }, + resumeSession: true, + }) + + // The client OCSP callback is an alternate certificate + // verification callback. + tests = append(tests, testCase{ + testType: clientTest, + name: "ClientOCSPCallback-Pass-" + vers.name, + config: Config{ + MaxVersion: vers.version, + Certificates: []Certificate{rsaCertificate}, + }, + tls13Variant: vers.tls13Variant, + flags: []string{ + "-enable-ocsp-stapling", + "-use-ocsp-callback", + }, + }) + var expectedLocalError string + if !config.async { + // TODO(davidben): Asynchronous fatal alerts are never + // sent. https://crbug.com/boringssl/130. + expectedLocalError = "remote error: bad certificate status response" + } + tests = append(tests, testCase{ + testType: clientTest, + name: "ClientOCSPCallback-Fail-" + vers.name, + config: Config{ + MaxVersion: vers.version, + Certificates: []Certificate{rsaCertificate}, + }, + tls13Variant: vers.tls13Variant, + flags: []string{ + "-enable-ocsp-stapling", + "-use-ocsp-callback", + "-fail-ocsp-callback", + }, + shouldFail: true, + expectedLocalError: expectedLocalError, + expectedError: ":OCSP_CB_ERROR:", + }) + // The callback does not run if the server does not send an + // OCSP response. + certNoStaple := rsaCertificate + certNoStaple.OCSPStaple = nil + tests = append(tests, testCase{ + testType: clientTest, + name: "ClientOCSPCallback-FailNoStaple-" + vers.name, + config: Config{ + MaxVersion: vers.version, + Certificates: []Certificate{certNoStaple}, + }, + tls13Variant: vers.tls13Variant, + flags: []string{ + "-enable-ocsp-stapling", + "-use-ocsp-callback", + "-fail-ocsp-callback", + }, + }) + + // The server OCSP callback is a legacy mechanism for + // configuring OCSP, used by unreliable server software. + tests = append(tests, testCase{ + testType: serverTest, + name: "ServerOCSPCallback-SetInCallback-" + vers.name, + config: Config{ + MaxVersion: vers.version, + }, + tls13Variant: vers.tls13Variant, + expectedOCSPResponse: testOCSPResponse, + flags: []string{ + "-use-ocsp-callback", + "-set-ocsp-in-callback", + "-ocsp-response", + base64.StdEncoding.EncodeToString(testOCSPResponse), + }, + resumeSession: true, + }) + + // The callback may decline OCSP, in which case we act as if + // the client did not support it, even if a response was + // configured. + tests = append(tests, testCase{ + testType: serverTest, + name: "ServerOCSPCallback-Decline-" + vers.name, + config: Config{ + MaxVersion: vers.version, + }, + tls13Variant: vers.tls13Variant, + expectedOCSPResponse: []byte{}, + flags: []string{ + "-use-ocsp-callback", + "-decline-ocsp-callback", + "-ocsp-response", + base64.StdEncoding.EncodeToString(testOCSPResponse), + }, + resumeSession: true, + }) + + // The callback may also signal an internal error. + tests = append(tests, testCase{ + testType: serverTest, + name: "ServerOCSPCallback-Fail-" + vers.name, + config: Config{ + MaxVersion: vers.version, + }, + tls13Variant: vers.tls13Variant, + flags: []string{ + "-use-ocsp-callback", + "-fail-ocsp-callback", + "-ocsp-response", + base64.StdEncoding.EncodeToString(testOCSPResponse), + }, + shouldFail: true, + expectedError: ":OCSP_CB_ERROR:", + }) + } // Certificate verification tests. for _, vers := range tlsVersions { diff --git a/ssl/test/test_config.cc b/ssl/test/test_config.cc index f50251d..3afb01b 100644 --- a/ssl/test/test_config.cc +++ b/ssl/test/test_config.cc @@ -133,6 +133,10 @@ const Flag kBoolFlags[] = { { "-expect-draft-downgrade", &TestConfig::expect_draft_downgrade }, { "-handoff", &TestConfig::handoff }, { "-expect-dummy-pq-padding", &TestConfig::expect_dummy_pq_padding }, + { "-use-ocsp-callback", &TestConfig::use_ocsp_callback }, + { "-set-ocsp-in-callback", &TestConfig::set_ocsp_in_callback }, + { "-decline-ocsp-callback", &TestConfig::decline_ocsp_callback }, + { "-fail-ocsp-callback", &TestConfig::fail_ocsp_callback }, }; const Flag kStringFlags[] = { diff --git a/ssl/test/test_config.h b/ssl/test/test_config.h index fb479d1..a9eec62 100644 --- a/ssl/test/test_config.h +++ b/ssl/test/test_config.h @@ -154,6 +154,10 @@ struct TestConfig { int dummy_pq_padding_len = 0; bool handoff = false; bool expect_dummy_pq_padding = false; + bool use_ocsp_callback = false; + bool set_ocsp_in_callback = false; + bool decline_ocsp_callback = false; + bool fail_ocsp_callback = false; }; bool ParseConfig(int argc, char **argv, TestConfig *out_initial, -- 2.7.4