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..cceacebb67f7cf46d0a4a13be90e309c6973fb73 100644 |
--- a/net/socket/ssl_client_socket_openssl.cc |
+++ b/net/socket/ssl_client_socket_openssl.cc |
@@ -319,6 +319,88 @@ 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: |
+ virtual ~PeerCertificateChain() {}; |
+ |
+ // Resets the current chain, freeing any resources, and updates the current |
+ // chain to be a referenced copy of the chain stored in |ssl|. |
+ // If |ssl| is NULL, then the current certificate chain will be freed. |
+ void Reset(SSL* ssl) { |
+ x509_certs_.clear(); |
+ der_certs_chain_.clear(); |
+ der_certs_mem_handler_.clear(); |
+ |
+ if (ssl == NULL) |
+ return; |
+ |
+ STACK_OF(X509)* intermediates = SSL_get_peer_cert_chain(ssl); |
+ if (intermediates) { |
+ for (int i = 0; i < sk_X509_num(intermediates); ++i) { |
+ if (!AddCert(sk_X509_value(intermediates, i))) { |
+ Reset(NULL); |
+ return; |
+ } |
+ } |
+ } |
+ } |
+ |
+ // Returns the current certificate chain as a vector of DER-encoded |
+ // base::StringPieces. The returned vector remains valid until Reset is |
+ // called. |
+ std::vector<base::StringPiece> AsStringPieceVector() const { |
+ return der_certs_chain_; |
+ } |
+ |
+ bool empty() const { return x509_certs_.empty(); } |
+ size_t size() const { return x509_certs_.size(); } |
+ |
+ X509* operator[](size_t index) const { |
+ DCHECK_LT(index, x509_certs_.size()); |
+ return x509_certs_[index].get(); |
+ } |
+ |
+ private: |
+ struct OpenSSLFreeDeleter { |
+ inline void operator()(void* ptr) const { OPENSSL_free(ptr); } |
+ }; |
+ |
+ struct X509Deleter { |
+ inline void operator()(X509* ptr) const { X509_free(ptr); } |
+ }; |
+ |
+ typedef scoped_ptr<unsigned char, OpenSSLFreeDeleter> ScopedOpenSSLArray; |
+ typedef scoped_ptr<X509, X509Deleter> ScopedX509; |
+ |
+ bool AddCert(X509* cert) { |
+ unsigned char* cert_data = NULL; |
+ int cert_data_length = i2d_X509(cert, &cert_data); |
+ if (cert_data_length <= 0 || !cert_data) |
+ return false; |
+ |
+ der_certs_mem_handler_.push_back(ScopedOpenSSLArray(cert_data)); |
+ der_certs_chain_.push_back(base::StringPiece( |
+ reinterpret_cast<char*>(cert_data), cert_data_length)); |
+ |
+ x509_certs_.push_back(ScopedX509(cert)); |
+ // Increase reference count for cert |
+ CRYPTO_add(&cert->references, 1, CRYPTO_LOCK_X509); |
+ |
+ return true; |
+ } |
+ |
+ std::vector<base::StringPiece> der_certs_chain_; |
+ |
+ // Make sure the DER data strings are free'd with OPENSSL_free |
+ std::vector<PeerCertificateChain::ScopedOpenSSLArray> der_certs_mem_handler_; |
+ |
+ std::vector<ScopedX509> x509_certs_; |
Ryan Sleevi
2014/02/25 20:27:09
Storing a vector of ScopedX509 doesn't work before
haavardm
2014/02/25 20:53:52
Seems good. I guess I got a little too focused on
haavardm
2014/03/11 18:43:20
Done.
|
+}; |
+ |
// static |
SSLSessionCacheOpenSSL::Config |
SSLClientSocketOpenSSL::SSLContext::kDefaultSessionCacheConfig = { |
@@ -347,6 +429,8 @@ SSLClientSocketOpenSSL::SSLClientSocketOpenSSL( |
weak_factory_(this), |
pending_read_error_(kNoPendingReadResult), |
transport_write_error_(OK), |
+ server_cert_chain_(new PeerCertificateChain()), |
+ server_cert_(NULL), |
completed_handshake_(false), |
client_auth_cert_needed_(false), |
cert_verifier_(context.cert_verifier), |
@@ -917,25 +1001,22 @@ 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_)); |
haavardm
2014/02/25 19:37:37
I removed this, as it seems that server certificat
|
- if (!cert.get()) { |
- LOG(WARNING) << "SSL_get_peer_certificate returned NULL"; |
- return NULL; |
- } |
- |
+#ifdef USE_OPENSSL |
// 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) |
+ for (int i = 1; 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_ = |
+ X509Certificate::CreateFromHandle(sk_X509_value(chain, 0), intermediates); |
Ryan Sleevi
2014/02/25 20:27:09
if |chain| is false, or sk_X509_num(chain) is 0, t
haavardm
2014/03/11 18:43:20
Done.
|
+#else |
+ server_cert_chain_->Reset(ssl_); |
+ server_cert_ = X509Certificate::CreateFromDERCertChain( |
+ server_cert_chain_->AsStringPieceVector()); |
+#endif |
return server_cert_.get(); |
} |