Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(152)

Unified Diff: net/socket/ssl_client_socket_openssl.cc

Issue 177143004: OpenSSL: don't allow the server certificate to change during renegotiation. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Address wtc's comments. Created 6 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « net/socket/ssl_client_socket_openssl.h ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: net/socket/ssl_client_socket_openssl.cc
diff --git a/net/socket/ssl_client_socket_openssl.cc b/net/socket/ssl_client_socket_openssl.cc
index b253dfeae50884859e2dceb3657363d2a6834b29..463b2c82d7ca4f4b8b6c658cc84275a740ce7b7d 100644
--- a/net/socket/ssl_client_socket_openssl.cc
+++ b/net/socket/ssl_client_socket_openssl.cc
@@ -183,6 +183,10 @@ int MapOpenSSLErrorSSL() {
case SSL_R_TLSV1_ALERT_RECORD_OVERFLOW:
case SSL_R_TLSV1_ALERT_USER_CANCELLED:
return ERR_SSL_PROTOCOL_ERROR;
+ case SSL_R_CERTIFICATE_VERIFY_FAILED:
+ // The only way that the certificate verify callback can fail is if
+ // the leaf certificate changed during a renegotiation.
+ return ERR_SSL_SERVER_CERT_CHANGED;
default:
LOG(WARNING) << "Unmapped error reason: " << ERR_GET_REASON(error_code);
return ERR_FAILED;
@@ -212,13 +216,6 @@ int MapOpenSSLError(int err, const crypto::OpenSSLErrStackTracer& tracer) {
}
}
-// We do certificate verification after handshake, so we disable the default
-// by registering a no-op verify function.
-int NoOpVerifyCallback(X509_STORE_CTX*, void *) {
- DVLOG(3) << "skipping cert verify";
- return 1;
-}
-
// Utility to construct the appropriate set & clear masks for use the OpenSSL
// options and mode configuration functions. (SSL_set_options etc)
struct SslSetClearMask {
@@ -270,9 +267,10 @@ class SSLClientSocketOpenSSL::SSLContext {
DCHECK_NE(ssl_socket_data_index_, -1);
ssl_ctx_.reset(SSL_CTX_new(SSLv23_client_method()));
session_cache_.Reset(ssl_ctx_.get(), kDefaultSessionCacheConfig);
- SSL_CTX_set_cert_verify_callback(ssl_ctx_.get(), NoOpVerifyCallback, NULL);
+ SSL_CTX_set_cert_verify_callback(ssl_ctx_.get(), CertVerifyCallback, NULL);
SSL_CTX_set_client_cert_cb(ssl_ctx_.get(), ClientCertCallback);
SSL_CTX_set_channel_id_cb(ssl_ctx_.get(), ChannelIDCallback);
+ SSL_CTX_set_verify(ssl_ctx_.get(), SSL_VERIFY_PEER, NULL);
#if defined(OPENSSL_NPN_NEGOTIATED)
// TODO(kristianm): Only select this if ssl_config_.next_proto is not empty.
// It would be better if the callback were not a global setting,
@@ -302,6 +300,15 @@ class SSLClientSocketOpenSSL::SSLContext {
socket->ChannelIDRequestCallback(ssl, pkey);
}
+ static int CertVerifyCallback(X509_STORE_CTX *store_ctx, void *arg) {
+ SSL* ssl = reinterpret_cast<SSL*>(X509_STORE_CTX_get_ex_data(
+ store_ctx, SSL_get_ex_data_X509_STORE_CTX_idx()));
+ SSLClientSocketOpenSSL* socket = GetInstance()->GetClientSocketFromSSL(ssl);
+ CHECK(socket);
+
+ return socket->CertVerifyCallback(store_ctx);
+ }
+
static int SelectNextProtoCallback(SSL* ssl,
unsigned char** out, unsigned char* outlen,
const unsigned char* in,
@@ -1372,6 +1379,43 @@ void SSLClientSocketOpenSSL::ChannelIDRequestCallback(SSL* ssl,
*pkey = EVP_PKEY_dup(ec_private_key->key());
}
+int SSLClientSocketOpenSSL::CertVerifyCallback(X509_STORE_CTX* store_ctx) {
+ if (!completed_handshake_) {
+ // If the first handshake hasn't completed then we accept any certificates
+ // because we verify after the handshake.
+ return 1;
+ }
+
+ std::string der_current_cert;
+ if (!X509Certificate::GetDEREncoded(server_cert_->os_cert_handle(),
+ &der_current_cert)) {
+ LOG(ERROR) << "Failed to get current certificate in DER form";
+ return 0;
+ }
+
+ X509* leaf_cert = sk_X509_value(store_ctx->chain, 0);
+ int len = i2d_X509(leaf_cert, NULL);
+ if (len < 0) {
+ LOG(ERROR) << "Failed to marshal certificate from renegotiation";
+ return 0;
+ }
+
+ scoped_ptr<uint8[]> der_leaf_cert(new uint8[len]);
+ uint8 *outp = der_leaf_cert.get();
+ len = i2d_X509(leaf_cert, &outp);
+
+ if (static_cast<size_t>(len) == der_current_cert.size() &&
+ memcmp(der_leaf_cert.get(),
+ der_current_cert.data(),
+ der_current_cert.size()) == 0) {
+ // The certificates match so the renegotiation can continue.
+ return 1;
+ }
Ryan Sleevi 2014/02/27 22:43:40 Why do this? Why not simply use if (!completed_ha
agl 2014/02/27 22:57:10 Yep, that works. Thanks!
+
+ LOG(ERROR) << "Server certificate changed between handshakes";
+ return 0;
+}
+
// SelectNextProtoCallback is called by OpenSSL during the handshake. If the
// server supports NPN, selects a protocol from the list that the server
// provides. According to third_party/openssl/openssl/ssl/ssl_lib.c, the
« no previous file with comments | « net/socket/ssl_client_socket_openssl.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698