| 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 2e011d97997d551dc780eded34b235bd7440278f..1a9d4b735fb40f2c5b6f205d4e16cd3807081af3 100644
|
| --- a/net/socket/ssl_client_socket_openssl.cc
|
| +++ b/net/socket/ssl_client_socket_openssl.cc
|
| @@ -27,6 +27,7 @@
|
| #include "net/cert/ct_verifier.h"
|
| #include "net/cert/single_request_cert_verifier.h"
|
| #include "net/cert/x509_certificate_net_log_param.h"
|
| +#include "net/cert/x509_util_openssl.h"
|
| #include "net/http/transport_security_state.h"
|
| #include "net/socket/ssl_session_cache_openssl.h"
|
| #include "net/ssl/openssl_ssl_util.h"
|
| @@ -251,7 +252,7 @@ class SSLClientSocketOpenSSL::PeerCertificateChain {
|
| void Reset(STACK_OF(X509)* chain);
|
|
|
| // Note that when USE_OPENSSL is defined, OSCertHandle is X509*
|
| - const scoped_refptr<X509Certificate>& AsOSChain() const { return os_chain_; }
|
| + scoped_refptr<X509Certificate> AsOSChain() const;
|
|
|
| size_t size() const {
|
| if (!openssl_chain_.get())
|
| @@ -259,17 +260,17 @@ class SSLClientSocketOpenSSL::PeerCertificateChain {
|
| return sk_X509_num(openssl_chain_.get());
|
| }
|
|
|
| - X509* operator[](size_t index) const {
|
| + bool empty() const {
|
| + return size() == 0;
|
| + }
|
| +
|
| + X509* Get(size_t index) const {
|
| DCHECK_LT(index, size());
|
| return sk_X509_value(openssl_chain_.get(), index);
|
| }
|
|
|
| - bool IsValid() { return os_chain_.get() && openssl_chain_.get(); }
|
| -
|
| private:
|
| ScopedX509Stack openssl_chain_;
|
| -
|
| - scoped_refptr<X509Certificate> os_chain_;
|
| };
|
|
|
| SSLClientSocketOpenSSL::PeerCertificateChain&
|
| @@ -278,69 +279,41 @@ SSLClientSocketOpenSSL::PeerCertificateChain::operator=(
|
| if (this == &other)
|
| return *this;
|
|
|
| - // os_chain_ is reference counted by scoped_refptr;
|
| - os_chain_ = other.os_chain_;
|
| -
|
| openssl_chain_.reset(X509_chain_up_ref(other.openssl_chain_.get()));
|
| -
|
| return *this;
|
| }
|
|
|
| -#if defined(USE_OPENSSL_CERTS)
|
| -// 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(
|
| STACK_OF(X509)* chain) {
|
| - openssl_chain_.reset(NULL);
|
| - os_chain_ = NULL;
|
| -
|
| - if (!chain)
|
| - return;
|
| -
|
| - X509Certificate::OSCertHandles intermediates;
|
| - for (size_t 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);
|
| -
|
| - openssl_chain_.reset(X509_chain_up_ref(chain));
|
| + openssl_chain_.reset(chain ? X509_chain_up_ref(chain) : NULL);
|
| }
|
| -#else // !defined(USE_OPENSSL_CERTS)
|
| -void SSLClientSocketOpenSSL::PeerCertificateChain::Reset(
|
| - STACK_OF(X509)* chain) {
|
| - openssl_chain_.reset(NULL);
|
| - os_chain_ = NULL;
|
| -
|
| - if (!chain)
|
| - return;
|
|
|
| - openssl_chain_.reset(X509_chain_up_ref(chain));
|
| +scoped_refptr<X509Certificate>
|
| +SSLClientSocketOpenSSL::PeerCertificateChain::AsOSChain() const {
|
| +#if defined(USE_OPENSSL_CERTS)
|
| + // When OSCertHandle is typedef'ed to X509, this implementation does a short
|
| + // cut to avoid converting back and forth between DER and the X509 struct.
|
| + X509Certificate::OSCertHandles intermediates;
|
| + for (size_t i = 1; i < sk_X509_num(openssl_chain_.get()); ++i) {
|
| + intermediates.push_back(sk_X509_value(openssl_chain_.get(), i));
|
| + }
|
|
|
| + return make_scoped_refptr(X509Certificate::CreateFromHandle(
|
| + sk_X509_value(openssl_chain_.get(), 0), intermediates));
|
| +#else
|
| + // DER-encode the chain and convert to a platform certificate handle.
|
| std::vector<base::StringPiece> der_chain;
|
| for (size_t i = 0; i < sk_X509_num(openssl_chain_.get()); ++i) {
|
| X509* x = sk_X509_value(openssl_chain_.get(), i);
|
| -
|
| - 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()));
|
| + base::StringPiece der;
|
| + if (!x509_util::GetDER(x, &der))
|
| + return NULL;
|
| + der_chain.push_back(der);
|
| }
|
|
|
| - if (der_chain.size() !=
|
| - static_cast<size_t>(sk_X509_num(openssl_chain_.get()))) {
|
| - openssl_chain_.reset(NULL);
|
| - os_chain_ = NULL;
|
| - }
|
| + return make_scoped_refptr(X509Certificate::CreateFromDERCertChain(der_chain));
|
| +#endif
|
| }
|
| -#endif // defined(USE_OPENSSL_CERTS)
|
|
|
| // static
|
| SSLSessionCacheOpenSSL::Config
|
| @@ -613,7 +586,7 @@ bool SSLClientSocketOpenSSL::UsingTCPFastOpen() const {
|
|
|
| bool SSLClientSocketOpenSSL::GetSSLInfo(SSLInfo* ssl_info) {
|
| ssl_info->Reset();
|
| - if (!server_cert_.get())
|
| + if (server_cert_chain_->empty())
|
| return false;
|
|
|
| ssl_info->cert = server_cert_verify_result_.verified_cert;
|
| @@ -961,12 +934,7 @@ int SSLClientSocketOpenSSL::DoHandshake() {
|
| set_signed_cert_timestamps_received(sct_list_len != 0);
|
|
|
| // Verify the certificate.
|
| - const bool got_cert = !!UpdateServerCert();
|
| - DCHECK(got_cert);
|
| - net_log_.AddEvent(
|
| - NetLog::TYPE_SSL_CERTIFICATES_RECEIVED,
|
| - base::Bind(&NetLogX509CertificateCallback,
|
| - base::Unretained(server_cert_.get())));
|
| + UpdateServerCert();
|
| GotoState(STATE_VERIFY_CERT);
|
| } else {
|
| int ssl_error = SSL_get_error(ssl_, rv);
|
| @@ -1049,12 +1017,20 @@ int SSLClientSocketOpenSSL::DoChannelIDLookupComplete(int result) {
|
| }
|
|
|
| int SSLClientSocketOpenSSL::DoVerifyCert(int result) {
|
| - DCHECK(server_cert_.get());
|
| + DCHECK(!server_cert_chain_->empty());
|
| DCHECK(start_cert_verification_time_.is_null());
|
| +
|
| GotoState(STATE_VERIFY_CERT_COMPLETE);
|
|
|
| + // If the certificate is bad and has been previously accepted, use
|
| + // the previous status and bypass the error.
|
| + base::StringPiece der_cert;
|
| + if (!x509_util::GetDER(server_cert_chain_->Get(0), &der_cert)) {
|
| + NOTREACHED();
|
| + return ERR_CERT_INVALID;
|
| + }
|
| CertStatus cert_status;
|
| - if (ssl_config_.IsAllowedBadCert(server_cert_.get(), &cert_status)) {
|
| + if (ssl_config_.IsAllowedBadCert(der_cert, &cert_status)) {
|
| VLOG(1) << "Received an expected bad cert with status: " << cert_status;
|
| server_cert_verify_result_.Reset();
|
| server_cert_verify_result_.cert_status = cert_status;
|
| @@ -1062,6 +1038,15 @@ int SSLClientSocketOpenSSL::DoVerifyCert(int result) {
|
| return OK;
|
| }
|
|
|
| + // When running in a sandbox, it may not be possible to create an
|
| + // X509Certificate*, as that may depend on OS functionality blocked
|
| + // in the sandbox.
|
| + if (!server_cert_.get()) {
|
| + server_cert_verify_result_.Reset();
|
| + server_cert_verify_result_.cert_status = CERT_STATUS_INVALID;
|
| + return ERR_CERT_INVALID;
|
| + }
|
| +
|
| start_cert_verification_time_ = base::TimeTicks::Now();
|
|
|
| int flags = 0;
|
| @@ -1147,14 +1132,16 @@ void SSLClientSocketOpenSSL::DoConnectCallback(int rv) {
|
| }
|
| }
|
|
|
| -X509Certificate* SSLClientSocketOpenSSL::UpdateServerCert() {
|
| +void SSLClientSocketOpenSSL::UpdateServerCert() {
|
| server_cert_chain_->Reset(SSL_get_peer_cert_chain(ssl_));
|
| server_cert_ = server_cert_chain_->AsOSChain();
|
|
|
| - if (!server_cert_chain_->IsValid())
|
| - DVLOG(1) << "UpdateServerCert received invalid certificate chain from peer";
|
| -
|
| - return server_cert_.get();
|
| + if (server_cert_.get()) {
|
| + net_log_.AddEvent(
|
| + NetLog::TYPE_SSL_CERTIFICATES_RECEIVED,
|
| + base::Bind(&NetLogX509CertificateCallback,
|
| + base::Unretained(server_cert_.get())));
|
| + }
|
| }
|
|
|
| void SSLClientSocketOpenSSL::VerifyCT() {
|
| @@ -1619,17 +1606,24 @@ int SSLClientSocketOpenSSL::CertVerifyCallback(X509_STORE_CTX* store_ctx) {
|
| return 1;
|
| }
|
|
|
| - CHECK(server_cert_.get());
|
| -
|
| - PeerCertificateChain chain(store_ctx->untrusted);
|
| - if (chain.IsValid() && server_cert_->Equals(chain.AsOSChain().get()))
|
| - return 1;
|
| -
|
| - if (!chain.IsValid())
|
| + // Disallow the server certificate to change in a renegotiation.
|
| + if (server_cert_chain_->empty()) {
|
| LOG(ERROR) << "Received invalid certificate chain between handshakes";
|
| - else
|
| + return 0;
|
| + }
|
| + base::StringPiece old_der, new_der;
|
| + if (store_ctx->cert == NULL ||
|
| + !x509_util::GetDER(server_cert_chain_->Get(0), &old_der) ||
|
| + !x509_util::GetDER(store_ctx->cert, &new_der)) {
|
| + LOG(ERROR) << "Failed to encode certificates";
|
| + return 0;
|
| + }
|
| + if (old_der != new_der) {
|
| LOG(ERROR) << "Server certificate changed between handshakes";
|
| - return 0;
|
| + return 0;
|
| + }
|
| +
|
| + return 1;
|
| }
|
|
|
| // SelectNextProtoCallback is called by OpenSSL during the handshake. If the
|
|
|