summaryrefslogtreecommitdiff
path: root/src/ssl/s3_both.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/ssl/s3_both.cc')
-rw-r--r--src/ssl/s3_both.cc52
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) {