169 lines
5.4 KiB
C++
169 lines
5.4 KiB
C++
// Copyright 2012 The Chromium Authors
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include "net/socket/ssl_client_socket.h"
|
|
|
|
#include <string>
|
|
|
|
#include "base/logging.h"
|
|
#include "base/observer_list.h"
|
|
#include "net/socket/ssl_client_socket_impl.h"
|
|
#include "net/socket/stream_socket.h"
|
|
#include "net/ssl/ssl_client_session_cache.h"
|
|
#include "net/ssl/ssl_key_logger.h"
|
|
|
|
namespace net {
|
|
|
|
SSLClientSocket::SSLClientSocket() = default;
|
|
|
|
// static
|
|
void SSLClientSocket::SetSSLKeyLogger(std::unique_ptr<SSLKeyLogger> logger) {
|
|
SSLClientSocketImpl::SetSSLKeyLogger(std::move(logger));
|
|
}
|
|
|
|
// static
|
|
std::vector<uint8_t> SSLClientSocket::SerializeNextProtos(
|
|
const NextProtoVector& next_protos) {
|
|
std::vector<uint8_t> wire_protos;
|
|
for (const NextProto next_proto : next_protos) {
|
|
const std::string proto = NextProtoToString(next_proto);
|
|
if (proto.size() > 255) {
|
|
LOG(WARNING) << "Ignoring overlong ALPN protocol: " << proto;
|
|
continue;
|
|
}
|
|
if (proto.size() == 0) {
|
|
LOG(WARNING) << "Ignoring empty ALPN protocol";
|
|
continue;
|
|
}
|
|
wire_protos.push_back(proto.size());
|
|
for (const char ch : proto) {
|
|
wire_protos.push_back(static_cast<uint8_t>(ch));
|
|
}
|
|
}
|
|
|
|
return wire_protos;
|
|
}
|
|
|
|
SSLClientContext::SSLClientContext(
|
|
SSLConfigService* ssl_config_service,
|
|
CertVerifier* cert_verifier,
|
|
TransportSecurityState* transport_security_state,
|
|
CTPolicyEnforcer* ct_policy_enforcer,
|
|
SSLClientSessionCache* ssl_client_session_cache,
|
|
SCTAuditingDelegate* sct_auditing_delegate)
|
|
: ssl_config_service_(ssl_config_service),
|
|
cert_verifier_(cert_verifier),
|
|
transport_security_state_(transport_security_state),
|
|
ct_policy_enforcer_(ct_policy_enforcer),
|
|
ssl_client_session_cache_(ssl_client_session_cache),
|
|
sct_auditing_delegate_(sct_auditing_delegate) {
|
|
CHECK(cert_verifier_);
|
|
CHECK(transport_security_state_);
|
|
CHECK(ct_policy_enforcer_);
|
|
|
|
if (ssl_config_service_) {
|
|
config_ = ssl_config_service_->GetSSLContextConfig();
|
|
ssl_config_service_->AddObserver(this);
|
|
}
|
|
cert_verifier_->AddObserver(this);
|
|
CertDatabase::GetInstance()->AddObserver(this);
|
|
}
|
|
|
|
SSLClientContext::~SSLClientContext() {
|
|
if (ssl_config_service_) {
|
|
ssl_config_service_->RemoveObserver(this);
|
|
}
|
|
cert_verifier_->RemoveObserver(this);
|
|
CertDatabase::GetInstance()->RemoveObserver(this);
|
|
}
|
|
|
|
std::unique_ptr<SSLClientSocket> SSLClientContext::CreateSSLClientSocket(
|
|
std::unique_ptr<StreamSocket> stream_socket,
|
|
const HostPortPair& host_and_port,
|
|
const SSLConfig& ssl_config) {
|
|
return std::make_unique<SSLClientSocketImpl>(this, std::move(stream_socket),
|
|
host_and_port, ssl_config);
|
|
}
|
|
|
|
bool SSLClientContext::GetClientCertificate(
|
|
const HostPortPair& server,
|
|
scoped_refptr<X509Certificate>* client_cert,
|
|
scoped_refptr<SSLPrivateKey>* private_key) {
|
|
return ssl_client_auth_cache_.Lookup(server, client_cert, private_key);
|
|
}
|
|
|
|
void SSLClientContext::SetClientCertificate(
|
|
const HostPortPair& server,
|
|
scoped_refptr<X509Certificate> client_cert,
|
|
scoped_refptr<SSLPrivateKey> private_key) {
|
|
ssl_client_auth_cache_.Add(server, std::move(client_cert),
|
|
std::move(private_key));
|
|
|
|
if (ssl_client_session_cache_) {
|
|
// Session resumption bypasses client certificate negotiation, so flush all
|
|
// associated sessions when preferences change.
|
|
ssl_client_session_cache_->FlushForServer(server);
|
|
}
|
|
NotifySSLConfigForServerChanged(server);
|
|
}
|
|
|
|
bool SSLClientContext::ClearClientCertificate(const HostPortPair& server) {
|
|
if (!ssl_client_auth_cache_.Remove(server)) {
|
|
return false;
|
|
}
|
|
|
|
if (ssl_client_session_cache_) {
|
|
// Session resumption bypasses client certificate negotiation, so flush all
|
|
// associated sessions when preferences change.
|
|
ssl_client_session_cache_->FlushForServer(server);
|
|
}
|
|
NotifySSLConfigForServerChanged(server);
|
|
return true;
|
|
}
|
|
|
|
void SSLClientContext::AddObserver(Observer* observer) {
|
|
observers_.AddObserver(observer);
|
|
}
|
|
|
|
void SSLClientContext::RemoveObserver(Observer* observer) {
|
|
observers_.RemoveObserver(observer);
|
|
}
|
|
|
|
void SSLClientContext::OnSSLContextConfigChanged() {
|
|
// TODO(davidben): Should we flush |ssl_client_session_cache_| here? We flush
|
|
// the socket pools, but not the session cache. While BoringSSL-based servers
|
|
// never change version or cipher negotiation based on client-offered
|
|
// sessions, other servers do.
|
|
config_ = ssl_config_service_->GetSSLContextConfig();
|
|
NotifySSLConfigChanged(SSLConfigChangeType::kSSLConfigChanged);
|
|
}
|
|
|
|
void SSLClientContext::OnCertVerifierChanged() {
|
|
NotifySSLConfigChanged(SSLConfigChangeType::kCertVerifierChanged);
|
|
}
|
|
|
|
void SSLClientContext::OnCertDBChanged() {
|
|
// Both the trust store and client certificate store may have changed.
|
|
ssl_client_auth_cache_.Clear();
|
|
if (ssl_client_session_cache_) {
|
|
ssl_client_session_cache_->Flush();
|
|
}
|
|
NotifySSLConfigChanged(SSLConfigChangeType::kCertDatabaseChanged);
|
|
}
|
|
|
|
void SSLClientContext::NotifySSLConfigChanged(SSLConfigChangeType change_type) {
|
|
for (Observer& observer : observers_) {
|
|
observer.OnSSLConfigChanged(change_type);
|
|
}
|
|
}
|
|
|
|
void SSLClientContext::NotifySSLConfigForServerChanged(
|
|
const HostPortPair& server) {
|
|
for (Observer& observer : observers_) {
|
|
observer.OnSSLConfigForServerChanged(server);
|
|
}
|
|
}
|
|
|
|
} // namespace net
|