Chromium Code Reviews| 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..71465b8165783d71fb847ea32a51a64b1366e62a 100644 |
| --- a/net/socket/ssl_client_socket_openssl.cc |
| +++ b/net/socket/ssl_client_socket_openssl.cc |
| @@ -319,6 +319,110 @@ 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 PeerCertificateChain { |
| + public: |
| + explicit PeerCertificateChain(SSL* ssl); |
| + PeerCertificateChain(const PeerCertificateChain& other); |
| + ~PeerCertificateChain(); |
| + PeerCertificateChain& operator=(const PeerCertificateChain& other); |
| + |
| + void Reset(SSL* ssl); |
| + const scoped_refptr<X509Certificate>& AsNativeChain() const { |
| + return native_chain_; |
| + } |
| + |
| + size_t size() const { |
| + if (!openssl_chain_.get()) |
| + return 0; |
| + return sk_X509_num(openssl_chain_.get()); |
| + } |
| + |
| + // Returns the certificate chain as presented by server. |
| + 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) * chain) { |
| + sk_X509_pop_free(chain, X509_free); |
| + } |
| + crypto::ScopedOpenSSL<STACK_OF(X509), FreeX509Stack> openssl_chain_; |
| + |
| + scoped_refptr<X509Certificate> native_chain_; |
| +}; |
| + |
| +PeerCertificateChain::PeerCertificateChain(SSL* ssl) { Reset(ssl); } |
| +PeerCertificateChain::~PeerCertificateChain() {}; |
| + |
| +PeerCertificateChain::PeerCertificateChain(const PeerCertificateChain& other) { |
| + *this = other; |
| +} |
| + |
| +PeerCertificateChain& PeerCertificateChain::operator=( |
| + const PeerCertificateChain& other) { |
| + if (this == &other) |
| + return *this; |
| + |
| + // native_chain is reference counted by scoped_refptr; |
| + native_chain_ = other.native_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; |
| +} |
| + |
| +void PeerCertificateChain::Reset(SSL* ssl) { |
| + openssl_chain_.reset(NULL); |
| + native_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); |
| + |
| + // We increase 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)); |
| + } |
| + |
| + native_chain_ = X509Certificate::CreateFromDERCertChain(der_chain); |
| + |
| + for (size_t i = 0; i < der_chain.size(); ++i) { |
| + OPENSSL_free((void*)der_chain[i].data()); |
|
Ryan Sleevi
2014/02/27 03:07:29
You shouldn't need to cast to void* here. If you d
haavardm
2014/02/27 10:46:28
chain[i].data() is 'const char*' while OPENSSL_Fre
wtc
2014/02/27 17:54:56
Yes, this requires a cast, and const_cast<char*> i
haavardm
2014/03/11 18:43:21
Done.
|
| + } |
| + |
| + if (!native_chain_->os_cert_handle() || |
| + native_chain_->GetIntermediateCertificates().size() + 1 != |
| + static_cast<size_t>(sk_X509_num(openssl_chain_.get()))) { |
|
Ryan Sleevi
2014/02/27 03:07:29
Pathological case: What happens when .size() == nu
haavardm
2014/02/27 10:46:28
I think we would have a crash or freeze long befor
haavardm
2014/03/11 18:43:21
Done.
|
| + openssl_chain_.reset(NULL); |
| + native_chain_ = NULL; |
| + } |
| +} |
| +; |
| + |
| // static |
| SSLSessionCacheOpenSSL::Config |
| SSLClientSocketOpenSSL::SSLContext::kDefaultSessionCacheConfig = { |
| @@ -347,6 +451,8 @@ SSLClientSocketOpenSSL::SSLClientSocketOpenSSL( |
| weak_factory_(this), |
| pending_read_error_(kNoPendingReadResult), |
| transport_write_error_(OK), |
| + server_cert_chain_(new PeerCertificateChain(NULL)), |
| + server_cert_(NULL), |
| completed_handshake_(false), |
| client_auth_cert_needed_(false), |
| cert_verifier_(context.cert_verifier), |
| @@ -362,8 +468,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,25 +1022,23 @@ 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; |
| - } |
| - |
| +#ifdef USE_OPENSSL |
| + server_cert_ = 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) |
| + for (int i = 1; i < sk_X509_num(chain); ++i) |
| intermediates.push_back(sk_X509_value(chain, i)); |
| + |
| + server_cert_ = X509Certificate::CreateFromHandle(sk_X509_value(chain, 0), |
| + intermediates); |
| } |
| - server_cert_ = X509Certificate::CreateFromHandle(cert.get(), intermediates); |
| - DCHECK(server_cert_.get()); |
| +#else |
| + server_cert_chain_->Reset(ssl_); |
| + server_cert_ = server_cert_chain_->AsNativeChain(); |
| +#endif |
| return server_cert_.get(); |
| } |