This also serves as a certificate verification callback for CRYPTO_BUFFER-based consumers. Remove the silly SSL_CTX_i_promise_to_verify_certs_after_the_handshake placeholder. Bug: 54, chromium:347402 Change-Id: I4c6b445cb9cd7204218acb2e5d1625e6f37aff6f Reviewed-on: https://boringssl-review.googlesource.com/17964 Reviewed-by: David Benjamin <davidben@google.com>kris/onging/CECPQ3_patch15
@@ -517,6 +517,13 @@ OPENSSL_EXPORT int SSL_get_error(const SSL *ssl, int ret_code); | |||||
* used to reuse the underlying connection for the retry. */ | * used to reuse the underlying connection for the retry. */ | ||||
#define SSL_ERROR_EARLY_DATA_REJECTED 15 | #define SSL_ERROR_EARLY_DATA_REJECTED 15 | ||||
/* SSL_ERROR_WANT_CERTIFICATE_VERIFY indicates the operation failed because | |||||
* certificate verification was incomplete. The caller may retry the operation | |||||
* when certificate verification is complete. | |||||
* | |||||
* See also |SSL_CTX_set_custom_verify|. */ | |||||
#define SSL_ERROR_WANT_CERTIFICATE_VERIFY 16 | |||||
/* SSL_set_mtu sets the |ssl|'s MTU in DTLS to |mtu|. It returns one on success | /* SSL_set_mtu sets the |ssl|'s MTU in DTLS to |mtu|. It returns one on success | ||||
* and zero on failure. */ | * and zero on failure. */ | ||||
OPENSSL_EXPORT int SSL_set_mtu(SSL *ssl, unsigned mtu); | OPENSSL_EXPORT int SSL_set_mtu(SSL *ssl, unsigned mtu); | ||||
@@ -2201,6 +2208,39 @@ OPENSSL_EXPORT void SSL_set_verify(SSL *ssl, int mode, | |||||
int (*callback)(int ok, | int (*callback)(int ok, | ||||
X509_STORE_CTX *store_ctx)); | X509_STORE_CTX *store_ctx)); | ||||
enum ssl_verify_result_t { | |||||
ssl_verify_ok, | |||||
ssl_verify_invalid, | |||||
ssl_verify_retry, | |||||
}; | |||||
/* SSL_CTX_set_custom_verify configures certificate verification. |mode| is one | |||||
* of the |SSL_VERIFY_*| values defined above. |callback| performs the | |||||
* certificate verification. | |||||
* | |||||
* The callback may call |SSL_get0_peer_certificates| for the certificate chain | |||||
* to validate. The callback should return |ssl_verify_ok| if the certificate is | |||||
* valid. If the certificate is invalid, the callback should return | |||||
* |ssl_verify_invalid| and optionally set |*out_alert| to an alert to send to | |||||
* the peer. Some useful alerts include |SSL_AD_CERTIFICATE_EXPIRED|, | |||||
* |SSL_AD_CERTIFICATE_REVOKED|, |SSL_AD_UNKNOWN_CA|, |SSL_AD_BAD_CERTIFICATE|, | |||||
* |SSL_AD_CERTIFICATE_UNKNOWN|, and |SSL_AD_INTERNAL_ERROR|. See RFC 5246 | |||||
* section 7.2.2 for their precise meanings. If unspecified, | |||||
* |SSL_AD_CERTIFICATE_UNKNOWN| will be sent by default. | |||||
* | |||||
* To verify a certificate asynchronously, the callback may return | |||||
* |ssl_verify_retry|. The handshake will then pause with |SSL_get_error| | |||||
* returning |SSL_ERROR_WANT_CERTIFICATE_VERIFY|. */ | |||||
OPENSSL_EXPORT void SSL_CTX_set_custom_verify( | |||||
SSL_CTX *ctx, int mode, | |||||
enum ssl_verify_result_t (*callback)(SSL *ssl, uint8_t *out_alert)); | |||||
/* SSL_set_custom_verify behaves like |SSL_CTX_set_custom_verify| but configures | |||||
* an individual |SSL|. */ | |||||
OPENSSL_EXPORT void SSL_set_custom_verify( | |||||
SSL *ssl, int mode, | |||||
enum ssl_verify_result_t (*callback)(SSL *ssl, uint8_t *out_alert)); | |||||
/* SSL_CTX_get_verify_mode returns |ctx|'s verify mode, set by | /* SSL_CTX_get_verify_mode returns |ctx|'s verify mode, set by | ||||
* |SSL_CTX_set_verify|. */ | * |SSL_CTX_set_verify|. */ | ||||
OPENSSL_EXPORT int SSL_CTX_get_verify_mode(const SSL_CTX *ctx); | OPENSSL_EXPORT int SSL_CTX_get_verify_mode(const SSL_CTX *ctx); | ||||
@@ -2320,13 +2360,6 @@ OPENSSL_EXPORT void SSL_CTX_set_cert_verify_callback( | |||||
SSL_CTX *ctx, int (*callback)(X509_STORE_CTX *store_ctx, void *arg), | SSL_CTX *ctx, int (*callback)(X509_STORE_CTX *store_ctx, void *arg), | ||||
void *arg); | void *arg); | ||||
/* SSL_CTX_i_promise_to_verify_certs_after_the_handshake indicates that the | |||||
* caller understands that the |CRYPTO_BUFFER|-based methods currently require | |||||
* post-handshake verification of certificates and thus it's ok to accept any | |||||
* certificates during the handshake. */ | |||||
OPENSSL_EXPORT void SSL_CTX_i_promise_to_verify_certs_after_the_handshake( | |||||
SSL_CTX *ctx); | |||||
/* SSL_enable_signed_cert_timestamps causes |ssl| (which must be the client end | /* SSL_enable_signed_cert_timestamps causes |ssl| (which must be the client end | ||||
* of a connection) to request SCTs from the server. See | * of a connection) to request SCTs from the server. See | ||||
* https://tools.ietf.org/html/rfc6962. | * https://tools.ietf.org/html/rfc6962. | ||||
@@ -3748,6 +3781,7 @@ OPENSSL_EXPORT void SSL_CTX_set_client_cert_cb( | |||||
#define SSL_PRIVATE_KEY_OPERATION 9 | #define SSL_PRIVATE_KEY_OPERATION 9 | ||||
#define SSL_PENDING_TICKET 10 | #define SSL_PENDING_TICKET 10 | ||||
#define SSL_EARLY_DATA_REJECTED 11 | #define SSL_EARLY_DATA_REJECTED 11 | ||||
#define SSL_CERTIFICATE_VERIFY 12 | |||||
/* SSL_want returns one of the above values to determine what the most recent | /* SSL_want returns one of the above values to determine what the most recent | ||||
* operation on |ssl| was blocked on. Use |SSL_get_error| instead. */ | * operation on |ssl| was blocked on. Use |SSL_get_error| instead. */ | ||||
@@ -4194,6 +4228,9 @@ struct ssl_ctx_st { | |||||
int (*app_verify_callback)(X509_STORE_CTX *store_ctx, void *arg); | int (*app_verify_callback)(X509_STORE_CTX *store_ctx, void *arg); | ||||
void *app_verify_arg; | void *app_verify_arg; | ||||
enum ssl_verify_result_t (*custom_verify_callback)(SSL *ssl, | |||||
uint8_t *out_alert); | |||||
/* Default password callback. */ | /* Default password callback. */ | ||||
pem_password_cb *default_passwd_callback; | pem_password_cb *default_passwd_callback; | ||||
@@ -4374,12 +4411,6 @@ struct ssl_ctx_st { | |||||
* otherwise. */ | * otherwise. */ | ||||
unsigned grease_enabled:1; | unsigned grease_enabled:1; | ||||
/* i_promise_to_verify_certs_after_the_handshake indicates that the | |||||
* application is using the |CRYPTO_BUFFER|-based methods and understands | |||||
* that this currently requires post-handshake verification of | |||||
* certificates. */ | |||||
unsigned i_promise_to_verify_certs_after_the_handshake:1; | |||||
/* allow_unknown_alpn_protos is one if the client allows unsolicited ALPN | /* allow_unknown_alpn_protos is one if the client allows unsolicited ALPN | ||||
* protocols from the peer. */ | * protocols from the peer. */ | ||||
unsigned allow_unknown_alpn_protos:1; | unsigned allow_unknown_alpn_protos:1; | ||||
@@ -334,6 +334,7 @@ OPENSSL_COMPILE_ASSERT( | |||||
/* server */ | /* server */ | ||||
/* extra state */ | /* extra state */ | ||||
#define SSL3_ST_SW_FLUSH (0x100 | SSL_ST_ACCEPT) | #define SSL3_ST_SW_FLUSH (0x100 | SSL_ST_ACCEPT) | ||||
#define SSL3_ST_VERIFY_CLIENT_CERT (0x101 | SSL_ST_ACCEPT) | |||||
/* read from client */ | /* read from client */ | ||||
#define SSL3_ST_SR_CLNT_HELLO_A (0x110 | SSL_ST_ACCEPT) | #define SSL3_ST_SR_CLNT_HELLO_A (0x110 | SSL_ST_ACCEPT) | ||||
#define SSL3_ST_SR_CLNT_HELLO_B (0x111 | SSL_ST_ACCEPT) | #define SSL3_ST_SR_CLNT_HELLO_B (0x111 | SSL_ST_ACCEPT) | ||||
@@ -173,7 +173,6 @@ static int dtls1_get_hello_verify_request(SSL_HANDSHAKE *hs); | |||||
static int ssl3_get_server_hello(SSL_HANDSHAKE *hs); | static int ssl3_get_server_hello(SSL_HANDSHAKE *hs); | ||||
static int ssl3_get_server_certificate(SSL_HANDSHAKE *hs); | static int ssl3_get_server_certificate(SSL_HANDSHAKE *hs); | ||||
static int ssl3_get_cert_status(SSL_HANDSHAKE *hs); | static int ssl3_get_cert_status(SSL_HANDSHAKE *hs); | ||||
static int ssl3_verify_server_cert(SSL_HANDSHAKE *hs); | |||||
static int ssl3_get_server_key_exchange(SSL_HANDSHAKE *hs); | static int ssl3_get_server_key_exchange(SSL_HANDSHAKE *hs); | ||||
static int ssl3_get_certificate_request(SSL_HANDSHAKE *hs); | static int ssl3_get_certificate_request(SSL_HANDSHAKE *hs); | ||||
static int ssl3_get_server_hello_done(SSL_HANDSHAKE *hs); | static int ssl3_get_server_hello_done(SSL_HANDSHAKE *hs); | ||||
@@ -292,9 +291,16 @@ int ssl3_connect(SSL_HANDSHAKE *hs) { | |||||
case SSL3_ST_VERIFY_SERVER_CERT: | case SSL3_ST_VERIFY_SERVER_CERT: | ||||
if (ssl_cipher_uses_certificate_auth(hs->new_cipher)) { | if (ssl_cipher_uses_certificate_auth(hs->new_cipher)) { | ||||
ret = ssl3_verify_server_cert(hs); | |||||
if (ret <= 0) { | |||||
goto end; | |||||
switch (ssl_verify_peer_cert(hs)) { | |||||
case ssl_verify_ok: | |||||
break; | |||||
case ssl_verify_invalid: | |||||
ret = -1; | |||||
goto end; | |||||
case ssl_verify_retry: | |||||
ssl->rwstate = SSL_CERTIFICATE_VERIFY; | |||||
ret = -1; | |||||
goto end; | |||||
} | } | ||||
} | } | ||||
hs->state = SSL3_ST_CR_KEY_EXCH_A; | hs->state = SSL3_ST_CR_KEY_EXCH_A; | ||||
@@ -1185,15 +1191,6 @@ static int ssl3_get_cert_status(SSL_HANDSHAKE *hs) { | |||||
return 1; | return 1; | ||||
} | } | ||||
static int ssl3_verify_server_cert(SSL_HANDSHAKE *hs) { | |||||
SSL *const ssl = hs->ssl; | |||||
if (!ssl->ctx->x509_method->session_verify_cert_chain(hs->new_session, ssl)) { | |||||
return -1; | |||||
} | |||||
return 1; | |||||
} | |||||
static int ssl3_get_server_key_exchange(SSL_HANDSHAKE *hs) { | static int ssl3_get_server_key_exchange(SSL_HANDSHAKE *hs) { | ||||
SSL *const ssl = hs->ssl; | SSL *const ssl = hs->ssl; | ||||
EC_KEY *ecdh = NULL; | EC_KEY *ecdh = NULL; | ||||
@@ -282,6 +282,23 @@ int ssl3_accept(SSL_HANDSHAKE *hs) { | |||||
goto end; | goto end; | ||||
} | } | ||||
} | } | ||||
hs->state = SSL3_ST_VERIFY_CLIENT_CERT; | |||||
break; | |||||
case SSL3_ST_VERIFY_CLIENT_CERT: | |||||
if (sk_CRYPTO_BUFFER_num(hs->new_session->certs) > 0) { | |||||
switch (ssl_verify_peer_cert(hs)) { | |||||
case ssl_verify_ok: | |||||
break; | |||||
case ssl_verify_invalid: | |||||
ret = -1; | |||||
goto end; | |||||
case ssl_verify_retry: | |||||
ssl->rwstate = SSL_CERTIFICATE_VERIFY; | |||||
ret = -1; | |||||
goto end; | |||||
} | |||||
} | |||||
hs->state = SSL3_ST_SR_KEY_EXCH_A; | hs->state = SSL3_ST_SR_KEY_EXCH_A; | ||||
break; | break; | ||||
@@ -1264,10 +1281,6 @@ static int ssl3_get_client_certificate(SSL_HANDSHAKE *hs) { | |||||
hs->new_session->peer_sha256_valid = 1; | hs->new_session->peer_sha256_valid = 1; | ||||
} | } | ||||
if (!ssl->ctx->x509_method->session_verify_cert_chain(hs->new_session, ssl)) { | |||||
return -1; | |||||
} | |||||
return 1; | return 1; | ||||
} | } | ||||
@@ -1007,6 +1007,7 @@ enum ssl_hs_wait_t { | |||||
ssl_hs_early_data_rejected, | ssl_hs_early_data_rejected, | ||||
ssl_hs_read_end_of_early_data, | ssl_hs_read_end_of_early_data, | ||||
ssl_hs_read_change_cipher_spec, | ssl_hs_read_change_cipher_spec, | ||||
ssl_hs_certificate_verify, | |||||
}; | }; | ||||
struct ssl_handshake_st { | struct ssl_handshake_st { | ||||
@@ -1341,6 +1342,9 @@ int ssl_parse_extensions(const CBS *cbs, uint8_t *out_alert, | |||||
const SSL_EXTENSION_TYPE *ext_types, | const SSL_EXTENSION_TYPE *ext_types, | ||||
size_t num_ext_types, int ignore_unknown); | size_t num_ext_types, int ignore_unknown); | ||||
/* ssl_verify_peer_cert verifies the peer certificate for |hs|. */ | |||||
enum ssl_verify_result_t ssl_verify_peer_cert(SSL_HANDSHAKE *hs); | |||||
/* SSLKEYLOGFILE functions. */ | /* SSLKEYLOGFILE functions. */ | ||||
@@ -1597,7 +1601,8 @@ struct ssl_x509_method_st { | |||||
/* session_verify_cert_chain verifies the certificate chain in |session|, | /* session_verify_cert_chain verifies the certificate chain in |session|, | ||||
* sets |session->verify_result| and returns one on success or zero on | * sets |session->verify_result| and returns one on success or zero on | ||||
* error. */ | * error. */ | ||||
int (*session_verify_cert_chain)(SSL_SESSION *session, SSL *ssl); | |||||
int (*session_verify_cert_chain)(SSL_SESSION *session, SSL *ssl, | |||||
uint8_t *out_alert); | |||||
/* hs_flush_cached_ca_names drops any cached |X509_NAME|s from |hs|. */ | /* hs_flush_cached_ca_names drops any cached |X509_NAME|s from |hs|. */ | ||||
void (*hs_flush_cached_ca_names)(SSL_HANDSHAKE *hs); | void (*hs_flush_cached_ca_names)(SSL_HANDSHAKE *hs); | ||||
@@ -1990,6 +1995,9 @@ struct ssl_st { | |||||
int (*verify_callback)(int ok, | int (*verify_callback)(int ok, | ||||
X509_STORE_CTX *ctx); /* fail if callback returns 0 */ | X509_STORE_CTX *ctx); /* fail if callback returns 0 */ | ||||
enum ssl_verify_result_t (*custom_verify_callback)(SSL *ssl, | |||||
uint8_t *out_alert); | |||||
void (*info_callback)(const SSL *ssl, int type, int value); | void (*info_callback)(const SSL *ssl, int type, int value); | ||||
/* Server-only: psk_identity_hint is the identity hint to send in | /* Server-only: psk_identity_hint is the identity hint to send in | ||||
@@ -830,3 +830,34 @@ int ssl_parse_extensions(const CBS *cbs, uint8_t *out_alert, | |||||
return 1; | return 1; | ||||
} | } | ||||
enum ssl_verify_result_t ssl_verify_peer_cert(SSL_HANDSHAKE *hs) { | |||||
SSL *const ssl = hs->ssl; | |||||
uint8_t alert = SSL_AD_CERTIFICATE_UNKNOWN; | |||||
enum ssl_verify_result_t ret; | |||||
if (ssl->custom_verify_callback != nullptr) { | |||||
ret = ssl->custom_verify_callback(ssl, &alert); | |||||
switch (ret) { | |||||
case ssl_verify_ok: | |||||
hs->new_session->verify_result = X509_V_OK; | |||||
break; | |||||
case ssl_verify_invalid: | |||||
hs->new_session->verify_result = X509_V_ERR_APPLICATION_VERIFICATION; | |||||
break; | |||||
case ssl_verify_retry: | |||||
break; | |||||
} | |||||
} else { | |||||
ret = ssl->ctx->x509_method->session_verify_cert_chain(hs->new_session, ssl, | |||||
&alert) | |||||
? ssl_verify_ok | |||||
: ssl_verify_invalid; | |||||
} | |||||
if (ret == ssl_verify_invalid) { | |||||
OPENSSL_PUT_ERROR(SSL, SSL_R_CERTIFICATE_VERIFY_FAILED); | |||||
ssl3_send_alert(ssl, SSL3_AL_FATAL, alert); | |||||
} | |||||
return ret; | |||||
} |
@@ -392,6 +392,7 @@ SSL *SSL_new(SSL_CTX *ctx) { | |||||
ssl->msg_callback_arg = ctx->msg_callback_arg; | ssl->msg_callback_arg = ctx->msg_callback_arg; | ||||
ssl->verify_mode = ctx->verify_mode; | ssl->verify_mode = ctx->verify_mode; | ||||
ssl->verify_callback = ctx->default_verify_callback; | ssl->verify_callback = ctx->default_verify_callback; | ||||
ssl->custom_verify_callback = ctx->custom_verify_callback; | |||||
ssl->retain_only_sha256_of_client_certs = | ssl->retain_only_sha256_of_client_certs = | ||||
ctx->retain_only_sha256_of_client_certs; | ctx->retain_only_sha256_of_client_certs; | ||||
@@ -984,6 +985,9 @@ int SSL_get_error(const SSL *ssl, int ret_code) { | |||||
case SSL_EARLY_DATA_REJECTED: | case SSL_EARLY_DATA_REJECTED: | ||||
return SSL_ERROR_EARLY_DATA_REJECTED; | return SSL_ERROR_EARLY_DATA_REJECTED; | ||||
case SSL_CERTIFICATE_VERIFY: | |||||
return SSL_ERROR_WANT_CERTIFICATE_VERIFY; | |||||
} | } | ||||
return SSL_ERROR_SYSCALL; | return SSL_ERROR_SYSCALL; | ||||
@@ -1554,12 +1558,22 @@ int SSL_get_servername_type(const SSL *ssl) { | |||||
return TLSEXT_NAMETYPE_host_name; | return TLSEXT_NAMETYPE_host_name; | ||||
} | } | ||||
void SSL_CTX_enable_signed_cert_timestamps(SSL_CTX *ctx) { | |||||
ctx->signed_cert_timestamps_enabled = 1; | |||||
void SSL_CTX_set_custom_verify( | |||||
SSL_CTX *ctx, int mode, | |||||
enum ssl_verify_result_t (*callback)(SSL *ssl, uint8_t *out_alert)) { | |||||
ctx->verify_mode = mode; | |||||
ctx->custom_verify_callback = callback; | |||||
} | } | ||||
void SSL_CTX_i_promise_to_verify_certs_after_the_handshake(SSL_CTX *ctx) { | |||||
ctx->i_promise_to_verify_certs_after_the_handshake = 1; | |||||
void SSL_set_custom_verify( | |||||
SSL *ssl, int mode, | |||||
enum ssl_verify_result_t (*callback)(SSL *ssl, uint8_t *out_alert)) { | |||||
ssl->verify_mode = mode; | |||||
ssl->custom_verify_callback = callback; | |||||
} | |||||
void SSL_CTX_enable_signed_cert_timestamps(SSL_CTX *ctx) { | |||||
ctx->signed_cert_timestamps_enabled = 1; | |||||
} | } | ||||
void SSL_enable_signed_cert_timestamps(SSL *ssl) { | void SSL_enable_signed_cert_timestamps(SSL *ssl) { | ||||
@@ -3276,7 +3276,11 @@ TEST(SSLTest, SetChainAndKey) { | |||||
ASSERT_TRUE(SSL_CTX_set_chain_and_key(server_ctx.get(), &chain[0], | ASSERT_TRUE(SSL_CTX_set_chain_and_key(server_ctx.get(), &chain[0], | ||||
chain.size(), key.get(), nullptr)); | chain.size(), key.get(), nullptr)); | ||||
SSL_CTX_i_promise_to_verify_certs_after_the_handshake(client_ctx.get()); | |||||
SSL_CTX_set_custom_verify( | |||||
client_ctx.get(), SSL_VERIFY_PEER, | |||||
[](SSL *ssl, uint8_t *out_alert) -> ssl_verify_result_t { | |||||
return ssl_verify_ok; | |||||
}); | |||||
bssl::UniquePtr<SSL> client, server; | bssl::UniquePtr<SSL> client, server; | ||||
ASSERT_TRUE(ConnectClientAndServer(&client, &server, client_ctx.get(), | ASSERT_TRUE(ConnectClientAndServer(&client, &server, client_ctx.get(), | ||||
@@ -620,7 +620,9 @@ static int ssl_verify_alarm_type(long type) { | |||||
} | } | ||||
static int ssl_crypto_x509_session_verify_cert_chain(SSL_SESSION *session, | static int ssl_crypto_x509_session_verify_cert_chain(SSL_SESSION *session, | ||||
SSL *ssl) { | |||||
SSL *ssl, | |||||
uint8_t *out_alert) { | |||||
*out_alert = SSL_AD_INTERNAL_ERROR; | |||||
STACK_OF(X509) *const cert_chain = session->x509_chain; | STACK_OF(X509) *const cert_chain = session->x509_chain; | ||||
if (cert_chain == NULL || sk_X509_num(cert_chain) == 0) { | if (cert_chain == NULL || sk_X509_num(cert_chain) == 0) { | ||||
return 0; | return 0; | ||||
@@ -666,8 +668,7 @@ static int ssl_crypto_x509_session_verify_cert_chain(SSL_SESSION *session, | |||||
/* If |SSL_VERIFY_NONE|, the error is non-fatal, but we keep the result. */ | /* If |SSL_VERIFY_NONE|, the error is non-fatal, but we keep the result. */ | ||||
if (verify_ret <= 0 && ssl->verify_mode != SSL_VERIFY_NONE) { | if (verify_ret <= 0 && ssl->verify_mode != SSL_VERIFY_NONE) { | ||||
ssl3_send_alert(ssl, SSL3_AL_FATAL, ssl_verify_alarm_type(ctx.error)); | |||||
OPENSSL_PUT_ERROR(SSL, SSL_R_CERTIFICATE_VERIFY_FAILED); | |||||
*out_alert = ssl_verify_alarm_type(ctx.error); | |||||
goto err; | goto err; | ||||
} | } | ||||
@@ -112,6 +112,7 @@ struct TestState { | |||||
bool alpn_select_done = false; | bool alpn_select_done = false; | ||||
bool is_resume = false; | bool is_resume = false; | ||||
bool early_callback_ready = false; | bool early_callback_ready = false; | ||||
bool custom_verify_ready = false; | |||||
}; | }; | ||||
static void TestStateExFree(void *parent, void *ptr, CRYPTO_EX_DATA *ad, | static void TestStateExFree(void *parent, void *ptr, CRYPTO_EX_DATA *ad, | ||||
@@ -684,27 +685,52 @@ static int CertCallback(SSL *ssl, void *arg) { | |||||
return 1; | return 1; | ||||
} | } | ||||
static int VerifySucceed(X509_STORE_CTX *store_ctx, void *arg) { | |||||
SSL* ssl = (SSL*)X509_STORE_CTX_get_ex_data(store_ctx, | |||||
SSL_get_ex_data_X509_STORE_CTX_idx()); | |||||
static bool CheckVerifyCallback(SSL *ssl) { | |||||
const TestConfig *config = GetTestConfig(ssl); | const TestConfig *config = GetTestConfig(ssl); | ||||
if (!config->expected_ocsp_response.empty()) { | if (!config->expected_ocsp_response.empty()) { | ||||
const uint8_t *data; | const uint8_t *data; | ||||
size_t len; | size_t len; | ||||
SSL_get0_ocsp_response(ssl, &data, &len); | SSL_get0_ocsp_response(ssl, &data, &len); | ||||
if (len == 0) { | if (len == 0) { | ||||
fprintf(stderr, "OCSP response not available in verify callback\n"); | fprintf(stderr, "OCSP response not available in verify callback\n"); | ||||
return 0; | |||||
return false; | |||||
} | } | ||||
} | } | ||||
return true; | |||||
} | |||||
static int CertVerifyCallback(X509_STORE_CTX *store_ctx, void *arg) { | |||||
SSL* ssl = (SSL*)X509_STORE_CTX_get_ex_data(store_ctx, | |||||
SSL_get_ex_data_X509_STORE_CTX_idx()); | |||||
const TestConfig *config = GetTestConfig(ssl); | |||||
if (!CheckVerifyCallback(ssl)) { | |||||
return 0; | |||||
} | |||||
if (config->verify_fail) { | |||||
store_ctx->error = X509_V_ERR_APPLICATION_VERIFICATION; | |||||
return 0; | |||||
} | |||||
return 1; | return 1; | ||||
} | } | ||||
static int VerifyFail(X509_STORE_CTX *store_ctx, void *arg) { | |||||
store_ctx->error = X509_V_ERR_APPLICATION_VERIFICATION; | |||||
return 0; | |||||
static ssl_verify_result_t CustomVerifyCallback(SSL *ssl, uint8_t *out_alert) { | |||||
const TestConfig *config = GetTestConfig(ssl); | |||||
if (!CheckVerifyCallback(ssl)) { | |||||
return ssl_verify_invalid; | |||||
} | |||||
if (config->async && !GetTestState(ssl)->custom_verify_ready) { | |||||
return ssl_verify_retry; | |||||
} | |||||
if (config->verify_fail) { | |||||
return ssl_verify_invalid; | |||||
} | |||||
return ssl_verify_ok; | |||||
} | } | ||||
static int NextProtosAdvertisedCallback(SSL *ssl, const uint8_t **out, | static int NextProtosAdvertisedCallback(SSL *ssl, const uint8_t **out, | ||||
@@ -1139,10 +1165,8 @@ static bssl::UniquePtr<SSL_CTX> SetupCtx(SSL_CTX *old_ctx, | |||||
return nullptr; | return nullptr; | ||||
} | } | ||||
if (config->verify_fail) { | |||||
SSL_CTX_set_cert_verify_callback(ssl_ctx.get(), VerifyFail, NULL); | |||||
} else { | |||||
SSL_CTX_set_cert_verify_callback(ssl_ctx.get(), VerifySucceed, NULL); | |||||
if (!config->use_custom_verify_callback) { | |||||
SSL_CTX_set_cert_verify_callback(ssl_ctx.get(), CertVerifyCallback, NULL); | |||||
} | } | ||||
if (!config->signed_cert_timestamps.empty() && | if (!config->signed_cert_timestamps.empty() && | ||||
@@ -1270,6 +1294,9 @@ static bool RetryAsync(SSL *ssl, int ret) { | |||||
case SSL_ERROR_WANT_PRIVATE_KEY_OPERATION: | case SSL_ERROR_WANT_PRIVATE_KEY_OPERATION: | ||||
test_state->private_key_retries++; | test_state->private_key_retries++; | ||||
return true; | return true; | ||||
case SSL_ERROR_WANT_CERTIFICATE_VERIFY: | |||||
test_state->custom_verify_ready = true; | |||||
return true; | |||||
default: | default: | ||||
return false; | return false; | ||||
} | } | ||||
@@ -1763,20 +1790,23 @@ static bool DoConnection(bssl::UniquePtr<SSL_SESSION> *out_session, | |||||
if (!config->use_old_client_cert_callback) { | if (!config->use_old_client_cert_callback) { | ||||
SSL_set_cert_cb(ssl.get(), CertCallback, nullptr); | SSL_set_cert_cb(ssl.get(), CertCallback, nullptr); | ||||
} | } | ||||
int mode = SSL_VERIFY_NONE; | |||||
if (config->require_any_client_certificate) { | if (config->require_any_client_certificate) { | ||||
SSL_set_verify(ssl.get(), SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, | |||||
NULL); | |||||
mode = SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT; | |||||
} | } | ||||
if (config->verify_peer) { | if (config->verify_peer) { | ||||
SSL_set_verify(ssl.get(), SSL_VERIFY_PEER, NULL); | |||||
mode = SSL_VERIFY_PEER; | |||||
} | } | ||||
if (config->verify_peer_if_no_obc) { | if (config->verify_peer_if_no_obc) { | ||||
// Set SSL_VERIFY_FAIL_IF_NO_PEER_CERT so testing whether client | // Set SSL_VERIFY_FAIL_IF_NO_PEER_CERT so testing whether client | ||||
// certificates were requested is easy. | // certificates were requested is easy. | ||||
SSL_set_verify(ssl.get(), | |||||
SSL_VERIFY_PEER | SSL_VERIFY_PEER_IF_NO_OBC | | |||||
SSL_VERIFY_FAIL_IF_NO_PEER_CERT, | |||||
NULL); | |||||
mode = SSL_VERIFY_PEER | SSL_VERIFY_PEER_IF_NO_OBC | | |||||
SSL_VERIFY_FAIL_IF_NO_PEER_CERT; | |||||
} | |||||
if (config->use_custom_verify_callback) { | |||||
SSL_set_custom_verify(ssl.get(), mode, CustomVerifyCallback); | |||||
} else if (mode != SSL_VERIFY_NONE) { | |||||
SSL_set_verify(ssl.get(), mode, NULL); | |||||
} | } | ||||
if (config->false_start) { | if (config->false_start) { | ||||
SSL_set_mode(ssl.get(), SSL_MODE_ENABLE_FALSE_START); | SSL_set_mode(ssl.get(), SSL_MODE_ENABLE_FALSE_START); | ||||
@@ -4553,47 +4553,49 @@ func addStateMachineCoverageTests(config stateMachineTestConfig) { | |||||
if config.protocol == dtls && !vers.hasDTLS { | if config.protocol == dtls && !vers.hasDTLS { | ||||
continue | continue | ||||
} | } | ||||
for _, testType := range []testType{clientTest, serverTest} { | |||||
suffix := "-Client" | |||||
if testType == serverTest { | |||||
suffix = "-Server" | |||||
} | |||||
suffix += "-" + vers.name | |||||
for _, useCustomCallback := range []bool{false, true} { | |||||
for _, testType := range []testType{clientTest, serverTest} { | |||||
suffix := "-Client" | |||||
if testType == serverTest { | |||||
suffix = "-Server" | |||||
} | |||||
suffix += "-" + vers.name | |||||
if useCustomCallback { | |||||
suffix += "-CustomCallback" | |||||
} | |||||
flag := "-verify-peer" | |||||
if testType == serverTest { | |||||
flag = "-require-any-client-certificate" | |||||
} | |||||
flags := []string{"-verify-peer"} | |||||
if testType == serverTest { | |||||
flags = append(flags, "-require-any-client-certificate") | |||||
} | |||||
if useCustomCallback { | |||||
flags = append(flags, "-use-custom-verify-callback") | |||||
} | |||||
tests = append(tests, testCase{ | |||||
testType: testType, | |||||
name: "CertificateVerificationSucceed" + suffix, | |||||
config: Config{ | |||||
MaxVersion: vers.version, | |||||
Certificates: []Certificate{rsaCertificate}, | |||||
}, | |||||
tls13Variant: vers.tls13Variant, | |||||
flags: []string{ | |||||
flag, | |||||
"-expect-verify-result", | |||||
}, | |||||
resumeSession: true, | |||||
}) | |||||
tests = append(tests, testCase{ | |||||
testType: testType, | |||||
name: "CertificateVerificationFail" + suffix, | |||||
config: Config{ | |||||
MaxVersion: vers.version, | |||||
Certificates: []Certificate{rsaCertificate}, | |||||
}, | |||||
tls13Variant: vers.tls13Variant, | |||||
flags: []string{ | |||||
flag, | |||||
"-verify-fail", | |||||
}, | |||||
shouldFail: true, | |||||
expectedError: ":CERTIFICATE_VERIFY_FAILED:", | |||||
}) | |||||
tests = append(tests, testCase{ | |||||
testType: testType, | |||||
name: "CertificateVerificationSucceed" + suffix, | |||||
config: Config{ | |||||
MaxVersion: vers.version, | |||||
Certificates: []Certificate{rsaCertificate}, | |||||
}, | |||||
tls13Variant: vers.tls13Variant, | |||||
flags: append([]string{"-expect-verify-result"}, flags...), | |||||
resumeSession: true, | |||||
}) | |||||
tests = append(tests, testCase{ | |||||
testType: testType, | |||||
name: "CertificateVerificationFail" + suffix, | |||||
config: Config{ | |||||
MaxVersion: vers.version, | |||||
Certificates: []Certificate{rsaCertificate}, | |||||
}, | |||||
tls13Variant: vers.tls13Variant, | |||||
flags: append([]string{"-verify-fail"}, flags...), | |||||
shouldFail: true, | |||||
expectedError: ":CERTIFICATE_VERIFY_FAILED:", | |||||
}) | |||||
} | |||||
} | } | ||||
// By default, the client is in a soft fail mode where the peer | // By default, the client is in a soft fail mode where the peer | ||||
@@ -129,6 +129,7 @@ const Flag<bool> kBoolFlags[] = { | |||||
{ "-handshake-twice", &TestConfig::handshake_twice }, | { "-handshake-twice", &TestConfig::handshake_twice }, | ||||
{ "-allow-unknown-alpn-protos", &TestConfig::allow_unknown_alpn_protos }, | { "-allow-unknown-alpn-protos", &TestConfig::allow_unknown_alpn_protos }, | ||||
{ "-enable-ed25519", &TestConfig::enable_ed25519 }, | { "-enable-ed25519", &TestConfig::enable_ed25519 }, | ||||
{ "-use-custom-verify-callback", &TestConfig::use_custom_verify_callback }, | |||||
}; | }; | ||||
const Flag<std::string> kStringFlags[] = { | const Flag<std::string> kStringFlags[] = { | ||||
@@ -144,6 +144,7 @@ struct TestConfig { | |||||
bool handshake_twice = false; | bool handshake_twice = false; | ||||
bool allow_unknown_alpn_protos = false; | bool allow_unknown_alpn_protos = false; | ||||
bool enable_ed25519 = false; | bool enable_ed25519 = false; | ||||
bool use_custom_verify_callback = false; | |||||
}; | }; | ||||
bool ParseConfig(int argc, char **argv, TestConfig *out_initial, | bool ParseConfig(int argc, char **argv, TestConfig *out_initial, | ||||
@@ -102,6 +102,11 @@ int tls13_handshake(SSL_HANDSHAKE *hs, int *out_early_return) { | |||||
hs->wait = ssl_hs_ok; | hs->wait = ssl_hs_ok; | ||||
return -1; | return -1; | ||||
case ssl_hs_certificate_verify: | |||||
ssl->rwstate = SSL_CERTIFICATE_VERIFY; | |||||
hs->wait = ssl_hs_ok; | |||||
return -1; | |||||
case ssl_hs_early_data_rejected: | case ssl_hs_early_data_rejected: | ||||
ssl->rwstate = SSL_EARLY_DATA_REJECTED; | ssl->rwstate = SSL_EARLY_DATA_REJECTED; | ||||
/* Cause |SSL_write| to start failing immediately. */ | /* Cause |SSL_write| to start failing immediately. */ | ||||
@@ -357,12 +362,6 @@ int tls13_process_certificate(SSL_HANDSHAKE *hs, int allow_anonymous) { | |||||
} | } | ||||
hs->new_session->peer_sha256_valid = retain_sha256; | hs->new_session->peer_sha256_valid = retain_sha256; | ||||
if (!ssl->ctx->x509_method->session_verify_cert_chain(hs->new_session, | |||||
ssl)) { | |||||
goto err; | |||||
} | |||||
ret = 1; | ret = 1; | ||||
err: | err: | ||||
@@ -494,6 +494,16 @@ static enum ssl_hs_wait_t do_process_server_certificate(SSL_HANDSHAKE *hs) { | |||||
static enum ssl_hs_wait_t do_process_server_certificate_verify( | static enum ssl_hs_wait_t do_process_server_certificate_verify( | ||||
SSL_HANDSHAKE *hs) { | SSL_HANDSHAKE *hs) { | ||||
SSL *const ssl = hs->ssl; | SSL *const ssl = hs->ssl; | ||||
switch (ssl_verify_peer_cert(hs)) { | |||||
case ssl_verify_ok: | |||||
break; | |||||
case ssl_verify_invalid: | |||||
return ssl_hs_error; | |||||
case ssl_verify_retry: | |||||
hs->tls13_state = state_process_server_certificate_verify; | |||||
return ssl_hs_certificate_verify; | |||||
} | |||||
if (!ssl_check_message_type(ssl, SSL3_MT_CERTIFICATE_VERIFY) || | if (!ssl_check_message_type(ssl, SSL3_MT_CERTIFICATE_VERIFY) || | ||||
!tls13_process_certificate_verify(hs) || | !tls13_process_certificate_verify(hs) || | ||||
!ssl_hash_current_message(hs)) { | !ssl_hash_current_message(hs)) { | ||||
@@ -766,6 +766,16 @@ static enum ssl_hs_wait_t do_process_client_certificate_verify( | |||||
return ssl_hs_ok; | return ssl_hs_ok; | ||||
} | } | ||||
switch (ssl_verify_peer_cert(hs)) { | |||||
case ssl_verify_ok: | |||||
break; | |||||
case ssl_verify_invalid: | |||||
return ssl_hs_error; | |||||
case ssl_verify_retry: | |||||
hs->tls13_state = state_process_client_certificate_verify; | |||||
return ssl_hs_certificate_verify; | |||||
} | |||||
if (!ssl_check_message_type(ssl, SSL3_MT_CERTIFICATE_VERIFY) || | if (!ssl_check_message_type(ssl, SSL3_MT_CERTIFICATE_VERIFY) || | ||||
!tls13_process_certificate_verify(hs) || | !tls13_process_certificate_verify(hs) || | ||||
!ssl_hash_current_message(hs)) { | !ssl_hash_current_message(hs)) { | ||||
@@ -240,15 +240,9 @@ static int ssl_noop_x509_session_dup(SSL_SESSION *new_session, | |||||
} | } | ||||
static void ssl_noop_x509_session_clear(SSL_SESSION *session) {} | static void ssl_noop_x509_session_clear(SSL_SESSION *session) {} | ||||
static int ssl_noop_x509_session_verify_cert_chain(SSL_SESSION *session, | static int ssl_noop_x509_session_verify_cert_chain(SSL_SESSION *session, | ||||
SSL *ssl) { | |||||
if (!ssl->ctx->i_promise_to_verify_certs_after_the_handshake) { | |||||
ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNKNOWN_CA); | |||||
OPENSSL_PUT_ERROR(SSL, SSL_R_CERTIFICATE_VERIFY_FAILED); | |||||
return 0; | |||||
} | |||||
session->verify_result = X509_V_OK; | |||||
return 1; | |||||
SSL *ssl, | |||||
uint8_t *out_alert) { | |||||
return 0; | |||||
} | } | ||||
static void ssl_noop_x509_hs_flush_cached_ca_names(SSL_HANDSHAKE *hs) {} | static void ssl_noop_x509_hs_flush_cached_ca_names(SSL_HANDSHAKE *hs) {} | ||||