// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "net/socket/ssl_host_info.h" #include "base/metrics/histogram.h" #include "base/pickle.h" #include "base/string_piece.h" #include "net/base/dns_util.h" #include "net/base/dnsrr_resolver.h" #include "net/base/ssl_config_service.h" #include "net/base/x509_certificate.h" #include "net/socket/ssl_client_socket.h" namespace net { SSLHostInfo::State::State() {} SSLHostInfo::State::~State() {} void SSLHostInfo::State::Clear() { certs.clear(); } SSLHostInfo::SSLHostInfo( const std::string& hostname, const SSLConfig& ssl_config, CertVerifier* cert_verifier) : cert_verification_complete_(false), cert_verification_error_(ERR_CERT_INVALID), hostname_(hostname), cert_parsing_failed_(false), cert_verification_callback_(NULL), rev_checking_enabled_(ssl_config.rev_checking_enabled), verify_ev_cert_(ssl_config.verify_ev_cert), verifier_(cert_verifier), callback_(new CancelableCompletionCallback( ALLOW_THIS_IN_INITIALIZER_LIST(this), &SSLHostInfo::VerifyCallback)), dnsrr_resolver_(NULL), dns_callback_(NULL), dns_handle_(DnsRRResolver::kInvalidHandle) { } SSLHostInfo::~SSLHostInfo() { if (dns_handle_ != DnsRRResolver::kInvalidHandle) { dnsrr_resolver_->CancelResolve(dns_handle_); delete dns_callback_; } } void SSLHostInfo::StartDnsLookup(DnsRRResolver* dnsrr_resolver) { dnsrr_resolver_ = dnsrr_resolver; // Note: currently disabled. } const SSLHostInfo::State& SSLHostInfo::state() const { return state_; } SSLHostInfo::State* SSLHostInfo::mutable_state() { return &state_; } bool SSLHostInfo::Parse(const std::string& data) { State* state = mutable_state(); state->Clear(); cert_verification_complete_ = false; bool r = ParseInner(data); if (!r) state->Clear(); return r; } bool SSLHostInfo::ParseInner(const std::string& data) { State* state = mutable_state(); Pickle p(data.data(), data.size()); void* iter = NULL; int num_der_certs; if (!p.ReadInt(&iter, &num_der_certs) || num_der_certs < 0) { return false; } for (int i = 0; i < num_der_certs; i++) { std::string der_cert; if (!p.ReadString(&iter, &der_cert)) return false; state->certs.push_back(der_cert); } std::string throwaway_string; bool throwaway_bool; if (!p.ReadString(&iter, &throwaway_string)) return false; if (!p.ReadBool(&iter, &throwaway_bool)) return false; if (throwaway_bool) { int throwaway_int; if (!p.ReadInt(&iter, &throwaway_int) || !p.ReadString(&iter, &throwaway_string)) { return false; } } if (!state->certs.empty()) { std::vector der_certs(state->certs.size()); for (size_t i = 0; i < state->certs.size(); i++) der_certs[i] = state->certs[i]; cert_ = X509Certificate::CreateFromDERCertChain(der_certs); if (cert_.get()) { int flags = 0; if (verify_ev_cert_) flags |= X509Certificate::VERIFY_EV_CERT; if (rev_checking_enabled_) flags |= X509Certificate::VERIFY_REV_CHECKING_ENABLED; VLOG(1) << "Kicking off verification for " << hostname_; verification_start_time_ = base::TimeTicks::Now(); verification_end_time_ = base::TimeTicks(); int rv = verifier_.Verify(cert_.get(), hostname_, flags, &cert_verify_result_, callback_); if (rv != ERR_IO_PENDING) VerifyCallback(rv); } else { cert_parsing_failed_ = true; DCHECK(!cert_verification_callback_); } } return true; } std::string SSLHostInfo::Serialize() const { Pickle p(sizeof(Pickle::Header)); static const unsigned kMaxCertificatesSize = 32 * 1024; unsigned der_certs_size = 0; for (std::vector::const_iterator i = state_.certs.begin(); i != state_.certs.end(); i++) { der_certs_size += i->size(); } // We don't care to save the certificates over a certain size. if (der_certs_size > kMaxCertificatesSize) return ""; if (!p.WriteInt(state_.certs.size())) return ""; for (std::vector::const_iterator i = state_.certs.begin(); i != state_.certs.end(); i++) { if (!p.WriteString(*i)) return ""; } if (!p.WriteString("") || !p.WriteBool(false)) { return ""; } return std::string(reinterpret_cast(p.data()), p.size()); } const CertVerifyResult& SSLHostInfo::cert_verify_result() const { return cert_verify_result_; } int SSLHostInfo::WaitForCertVerification(CompletionCallback* callback) { if (cert_verification_complete_) return cert_verification_error_; DCHECK(!cert_parsing_failed_); DCHECK(!cert_verification_callback_); DCHECK(!state_.certs.empty()); cert_verification_callback_ = callback; return ERR_IO_PENDING; } void SSLHostInfo::VerifyCallback(int rv) { DCHECK(!verification_start_time_.is_null()); base::TimeTicks now = base::TimeTicks::Now(); const base::TimeDelta duration = now - verification_start_time(); UMA_HISTOGRAM_TIMES("Net.SSLHostInfoVerificationTimeMs", duration); VLOG(1) << "Verification took " << duration.InMilliseconds() << "ms"; verification_end_time_ = now; cert_verification_complete_ = true; cert_verification_error_ = rv; if (cert_verification_callback_) { CompletionCallback* callback = cert_verification_callback_; cert_verification_callback_ = NULL; callback->Run(rv); } } SSLHostInfoFactory::~SSLHostInfoFactory() {} } // namespace net