diff options
Diffstat (limited to 'src/ssl/s3_both.cc')
-rw-r--r-- | src/ssl/s3_both.cc | 52 |
1 files changed, 52 insertions, 0 deletions
diff --git a/src/ssl/s3_both.cc b/src/ssl/s3_both.cc index 9c4aa7ff..f51af697 100644 --- a/src/ssl/s3_both.cc +++ b/src/ssl/s3_both.cc @@ -137,6 +137,7 @@ SSL_HANDSHAKE::SSL_HANDSHAKE(SSL *ssl_arg) scts_requested(0), needs_psk_binder(0), received_hello_retry_request(0), + received_custom_extension(0), accept_psk_mode(0), cert_request(0), certificate_status_expected(0), @@ -851,8 +852,59 @@ int ssl_parse_extensions(const CBS *cbs, uint8_t *out_alert, return 1; } +static void set_crypto_buffer(CRYPTO_BUFFER **dest, CRYPTO_BUFFER *src) { + /* TODO(davidben): Remove this helper once |SSL_SESSION| can use |UniquePtr| + * and |UniquePtr| has up_ref helpers. */ + CRYPTO_BUFFER_free(*dest); + *dest = src; + if (src != nullptr) { + CRYPTO_BUFFER_up_ref(src); + } +} + enum ssl_verify_result_t ssl_verify_peer_cert(SSL_HANDSHAKE *hs) { SSL *const ssl = hs->ssl; + const SSL_SESSION *prev_session = ssl->s3->established_session; + if (prev_session != NULL) { + /* If renegotiating, the server must not change the server certificate. See + * https://mitls.org/pages/attacks/3SHAKE. We never resume on renegotiation, + * so this check is sufficient to ensure the reported peer certificate never + * changes on renegotiation. */ + assert(!ssl->server); + if (sk_CRYPTO_BUFFER_num(prev_session->certs) != + sk_CRYPTO_BUFFER_num(hs->new_session->certs)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_SERVER_CERT_CHANGED); + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER); + return ssl_verify_invalid; + } + + for (size_t i = 0; i < sk_CRYPTO_BUFFER_num(hs->new_session->certs); i++) { + const CRYPTO_BUFFER *old_cert = + sk_CRYPTO_BUFFER_value(prev_session->certs, i); + const CRYPTO_BUFFER *new_cert = + sk_CRYPTO_BUFFER_value(hs->new_session->certs, i); + if (CRYPTO_BUFFER_len(old_cert) != CRYPTO_BUFFER_len(new_cert) || + OPENSSL_memcmp(CRYPTO_BUFFER_data(old_cert), + CRYPTO_BUFFER_data(new_cert), + CRYPTO_BUFFER_len(old_cert)) != 0) { + OPENSSL_PUT_ERROR(SSL, SSL_R_SERVER_CERT_CHANGED); + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER); + return ssl_verify_invalid; + } + } + + /* The certificate is identical, so we may skip re-verifying the + * certificate. Since we only authenticated the previous one, copy other + * authentication from the established session and ignore what was newly + * received. */ + set_crypto_buffer(&hs->new_session->ocsp_response, + prev_session->ocsp_response); + set_crypto_buffer(&hs->new_session->signed_cert_timestamp_list, + prev_session->signed_cert_timestamp_list); + hs->new_session->verify_result = prev_session->verify_result; + return ssl_verify_ok; + } + uint8_t alert = SSL_AD_CERTIFICATE_UNKNOWN; enum ssl_verify_result_t ret; if (ssl->custom_verify_callback != nullptr) { |