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..cab634726faedff9e469654d7859889c269634f4 100644 |
--- a/net/socket/ssl_client_socket_openssl.cc |
+++ b/net/socket/ssl_client_socket_openssl.cc |
@@ -319,6 +319,140 @@ class SSLClientSocketOpenSSL::SSLContext { |
SSLSessionCacheOpenSSL session_cache_; |
}; |
+// PeerCertificateChain is a helper object which extracts the certificate |
+// chain, as given by the server, from an OpenSSL socket and performs the needed |
+// resource management. The first element of the chain is the leaf certificate |
+// and the other elements are in the order given by the server. |
+class SSLClientSocketOpenSSL::PeerCertificateChain { |
+ public: |
+ explicit PeerCertificateChain(SSL* ssl) { Reset(ssl); } |
+ PeerCertificateChain(const PeerCertificateChain& other) { *this = other; } |
+ ~PeerCertificateChain() {} |
+ PeerCertificateChain& operator=(const PeerCertificateChain& other); |
+ |
+ // Resets the PeerCertificateChain to the set of certificates supplied by the |
+ // peer of |ssl|, which may be NULL, indicating to empty the store |
+ // certificates. Note: If an error occurs, such as being unable to parse the |
+ // certificates, this will behave as if Reset(NULL) was called. |
+ void Reset(SSL* ssl); |
+ // Note that when USE_OPENSSL is defined, OSCertHandle is X509* |
+ const scoped_refptr<X509Certificate>& AsOSChain() const { return os_chain_; } |
+ |
+ size_t size() const { |
+ if (!openssl_chain_.get()) |
+ return 0; |
+ return sk_X509_num(openssl_chain_.get()); |
+ } |
+ |
+ X509* operator[](size_t index) const { |
+ DCHECK_LT(index, size()); |
+ return sk_X509_value(openssl_chain_.get(), index); |
+ } |
+ |
+ private: |
+ static void FreeX509Stack(STACK_OF(X509)* cert_chain) { |
+ sk_X509_pop_free(cert_chain, X509_free); |
+ } |
+ |
+ friend class crypto::ScopedOpenSSL<STACK_OF(X509), FreeX509Stack>; |
+ |
+ crypto::ScopedOpenSSL<STACK_OF(X509), FreeX509Stack> openssl_chain_; |
+ |
+ scoped_refptr<X509Certificate> os_chain_; |
+}; |
+ |
+SSLClientSocketOpenSSL::PeerCertificateChain& |
+SSLClientSocketOpenSSL::PeerCertificateChain::operator=( |
+ const PeerCertificateChain& other) { |
+ if (this == &other) |
+ return *this; |
+ |
+ // os_chain_ is reference counted by scoped_refptr; |
+ os_chain_ = other.os_chain_; |
+ |
+ // Must increase the reference count manually for sk_X509_dup |
+ openssl_chain_.reset(sk_X509_dup(other.openssl_chain_.get())); |
+ for (int i = 0; i < sk_X509_num(openssl_chain_.get()); ++i) { |
+ X509* x = sk_X509_value(openssl_chain_.get(), i); |
+ CRYPTO_add(&x->references, 1, CRYPTO_LOCK_X509); |
+ } |
+ return *this; |
+} |
+ |
+#if defined(USE_OPENSSL) |
+// When OSCertHandle is typedef'ed to X509, this implementation does a short cut |
+// to avoid converting back and forth between der and X509 struct. |
+void SSLClientSocketOpenSSL::PeerCertificateChain::Reset(SSL* ssl) { |
+ openssl_chain_.reset(NULL); |
+ os_chain_ = NULL; |
+ |
+ if (ssl == NULL) |
+ return; |
+ |
+ STACK_OF(X509)* chain = SSL_get_peer_cert_chain(ssl); |
+ if (!chain) |
+ return; |
+ |
+ X509Certificate::OSCertHandles intermediates; |
+ for (int i = 1; i < sk_X509_num(chain); ++i) |
+ intermediates.push_back(sk_X509_value(chain, i)); |
+ |
+ os_chain_ = |
+ X509Certificate::CreateFromHandle(sk_X509_value(chain, 0), intermediates); |
+ |
+ // sk_X509_dup does not increase reference count on the certs in the stack. |
+ openssl_chain_.reset(sk_X509_dup(chain)); |
+ |
+ std::vector<base::StringPiece> der_chain; |
+ for (int i = 0; i < sk_X509_num(openssl_chain_.get()); ++i) { |
+ X509* x = sk_X509_value(openssl_chain_.get(), i); |
+ // Increase the reference count for the certs in openssl_chain_. |
+ CRYPTO_add(&x->references, 1, CRYPTO_LOCK_X509); |
+ } |
+} |
+#else // !defined(USE_OPENSSL) |
+void SSLClientSocketOpenSSL::PeerCertificateChain::Reset(SSL* ssl) { |
+ openssl_chain_.reset(NULL); |
+ os_chain_ = NULL; |
+ |
+ if (ssl == NULL) |
+ return; |
+ |
+ STACK_OF(X509)* chain = SSL_get_peer_cert_chain(ssl); |
+ if (!chain) |
+ return; |
+ |
+ // sk_X509_dup does not increase reference count on the certs in the stack. |
+ openssl_chain_.reset(sk_X509_dup(chain)); |
+ |
+ std::vector<base::StringPiece> der_chain; |
+ for (int i = 0; i < sk_X509_num(openssl_chain_.get()); ++i) { |
+ X509* x = sk_X509_value(openssl_chain_.get(), i); |
+ |
+ // Increase the reference count for the certs in openssl_chain_. |
+ CRYPTO_add(&x->references, 1, CRYPTO_LOCK_X509); |
+ |
+ unsigned char* cert_data = NULL; |
+ int cert_data_length = i2d_X509(x, &cert_data); |
+ if (cert_data_length && cert_data) |
+ der_chain.push_back(base::StringPiece(reinterpret_cast<char*>(cert_data), |
+ cert_data_length)); |
+ } |
+ |
+ os_chain_ = X509Certificate::CreateFromDERCertChain(der_chain); |
+ |
+ for (size_t i = 0; i < der_chain.size(); ++i) { |
+ OPENSSL_free(const_cast<char*>(der_chain[i].data())); |
+ } |
+ |
+ if (der_chain.size() != |
+ static_cast<size_t>(sk_X509_num(openssl_chain_.get()))) { |
+ openssl_chain_.reset(NULL); |
+ os_chain_ = NULL; |
+ } |
+} |
+#endif // USE_OPENSSL |
+ |
// static |
SSLSessionCacheOpenSSL::Config |
SSLClientSocketOpenSSL::SSLContext::kDefaultSessionCacheConfig = { |
@@ -347,6 +481,7 @@ SSLClientSocketOpenSSL::SSLClientSocketOpenSSL( |
weak_factory_(this), |
pending_read_error_(kNoPendingReadResult), |
transport_write_error_(OK), |
+ server_cert_chain_(new PeerCertificateChain(NULL)), |
completed_handshake_(false), |
client_auth_cert_needed_(false), |
cert_verifier_(context.cert_verifier), |
@@ -362,8 +497,7 @@ SSLClientSocketOpenSSL::SSLClientSocketOpenSSL( |
npn_status_(kNextProtoUnsupported), |
channel_id_request_return_value_(ERR_UNEXPECTED), |
channel_id_xtn_negotiated_(false), |
- net_log_(transport_->socket()->NetLog()) { |
-} |
+ net_log_(transport_->socket()->NetLog()) {} |
SSLClientSocketOpenSSL::~SSLClientSocketOpenSSL() { |
Disconnect(); |
@@ -917,26 +1051,8 @@ void SSLClientSocketOpenSSL::DoConnectCallback(int rv) { |
} |
X509Certificate* SSLClientSocketOpenSSL::UpdateServerCert() { |
- if (server_cert_.get()) |
- return server_cert_.get(); |
- |
- crypto::ScopedOpenSSL<X509, X509_free> cert(SSL_get_peer_certificate(ssl_)); |
- if (!cert.get()) { |
- LOG(WARNING) << "SSL_get_peer_certificate returned NULL"; |
- return NULL; |
- } |
- |
- // Unlike SSL_get_peer_certificate, SSL_get_peer_cert_chain does not |
- // increment the reference so sk_X509_free does not need to be called. |
- STACK_OF(X509)* chain = SSL_get_peer_cert_chain(ssl_); |
- X509Certificate::OSCertHandles intermediates; |
- if (chain) { |
- for (int i = 0; i < sk_X509_num(chain); ++i) |
- intermediates.push_back(sk_X509_value(chain, i)); |
- } |
- server_cert_ = X509Certificate::CreateFromHandle(cert.get(), intermediates); |
- DCHECK(server_cert_.get()); |
- |
+ server_cert_chain_->Reset(ssl_); |
+ server_cert_ = server_cert_chain_->AsOSChain(); |
return server_cert_.get(); |
} |
@@ -1424,4 +1540,9 @@ int SSLClientSocketOpenSSL::SelectNextProtoCallback(unsigned char** out, |
return SSL_TLSEXT_ERR_OK; |
} |
+scoped_refptr<X509Certificate> |
+SSLClientSocketOpenSSL::GetUnverifiedServerCertificateChain() const { |
+ return server_cert_; |
+} |
+ |
} // namespace net |