std::vector<base::StringPiece> AsStringPieceVector() const;
bool empty() const { return certs_.empty(); }
- size_t size() const { return certs_.size(); }
CERTCertificate* operator[](size_t index) const {
DCHECK_LT(index, certs_.size());
void Reset() {
next_proto_status = SSLClientSocket::kNextProtoUnsupported;
next_proto.clear();
- server_protos.clear();
channel_id_sent = false;
server_cert_chain.Reset(NULL);
server_cert = NULL;
// negotiated protocol stored in |next_proto|.
SSLClientSocket::NextProtoStatus next_proto_status;
std::string next_proto;
- // If the server supports NPN, the protocols supported by the server.
- std::string server_protos;
// True if a channel ID was sent.
bool channel_id_sent;
}
}
-// Map NSS error code from the first SSL handshake to network error code.
-int MapNSSClientHandshakeError(PRErrorCode err) {
- switch (err) {
- // If the server closed on us, it is a protocol error.
- // Some TLS-intolerant servers do this when we request TLS.
- case PR_END_OF_FILE_ERROR:
- return ERR_SSL_PROTOCOL_ERROR;
- default:
- return MapNSSClientError(err);
- }
-}
-
} // namespace
// SSLClientSocketNSS::Core provides a thread-safe, ref-counted core that is
// 2) NSS Task Runner: Prepare data to go from NSS to an IO function:
// (BufferRecv, BufferSend)
// 3) Network Task Runner: Perform IO on that data (DoBufferRecv,
-// DoBufferSend, DoGetDomainBoundCert, OnGetDomainBoundCertComplete)
+// DoBufferSend, DoGetChannelID, OnGetChannelIDComplete)
// 4) Both Task Runners: Callback for asynchronous completion or to marshal
// data from the network task runner back to NSS (BufferRecvComplete,
// BufferSendComplete, OnHandshakeIOComplete)
// that their lifetimes match that of the owning SSLClientSocketNSS.
//
// The caller retains ownership of |transport|, |net_log|, and
- // |server_bound_cert_service|, and they will not be accessed once Detach()
+ // |channel_id_service|, and they will not be accessed once Detach()
// has been called.
Core(base::SequencedTaskRunner* network_task_runner,
base::SequencedTaskRunner* nss_task_runner,
const HostPortPair& host_and_port,
const SSLConfig& ssl_config,
BoundNetLog* net_log,
- ServerBoundCertService* server_bound_cert_service);
+ ChannelIDService* channel_id_service);
// Called on the network task runner.
// Transfers ownership of |socket|, an NSS SSL socket, and |buffers|, the
int Write(IOBuffer* buf, int buf_len, const CompletionCallback& callback);
// Called on the network task runner.
- bool IsConnected();
- bool HasPendingAsyncOperation();
- bool HasUnhandledReceivedData();
+ bool IsConnected() const;
+ bool HasPendingAsyncOperation() const;
+ bool HasUnhandledReceivedData() const;
+ bool WasEverUsed() const;
// Called on the network task runner.
// Causes the associated SSL/TLS session ID to be added to NSS's session
// Handles an NSS error generated while handshaking or performing IO.
// Returns a network error code mapped from the original NSS error.
- int HandleNSSError(PRErrorCode error, bool handshake_error);
+ int HandleNSSError(PRErrorCode error);
int DoHandshakeLoop(int last_io_result);
int DoReadLoop(int result);
// key into a SECKEYPublicKey and SECKEYPrivateKey. Returns OK upon success
// and an error code otherwise.
// Requires |domain_bound_private_key_| and |domain_bound_cert_| to have been
- // set by a call to ServerBoundCertService->GetDomainBoundCert. The caller
+ // set by a call to ChannelIDService->GetChannelID. The caller
// takes ownership of the |*cert| and |*key|.
int ImportChannelIDKeys(SECKEYPublicKey** public_key, SECKEYPrivateKey** key);
////////////////////////////////////////////////////////////////////////////
int DoBufferRecv(IOBuffer* buffer, int len);
int DoBufferSend(IOBuffer* buffer, int len);
- int DoGetDomainBoundCert(const std::string& host);
+ int DoGetChannelID(const std::string& host);
- void OnGetDomainBoundCertComplete(int result);
+ void OnGetChannelIDComplete(int result);
void OnHandshakeStateUpdated(const HandshakeState& state);
void OnNSSBufferUpdated(int amount_in_read_buffer);
void DidNSSRead(int result);
HandshakeState network_handshake_state_;
// The service for retrieving Channel ID keys. May be NULL.
- ServerBoundCertService* server_bound_cert_service_;
- ServerBoundCertService::RequestHandle domain_bound_cert_request_handle_;
+ ChannelIDService* channel_id_service_;
+ ChannelIDService::RequestHandle domain_bound_cert_request_handle_;
// The information about NSS task runner.
int unhandled_buffer_size_;
bool nss_waiting_write_;
bool nss_is_closed_;
+ // Set when Read() or Write() successfully reads or writes data to or from the
+ // network.
+ bool was_ever_used_;
+
////////////////////////////////////////////////////////////////////////////
// Members that are ONLY accessed on the NSS task runner:
////////////////////////////////////////////////////////////////////////////
// for the network task runner from the NSS task runner.
base::WeakPtr<BoundNetLog> weak_net_log_;
- // Written on the network task runner by the |server_bound_cert_service_|,
+ // Written on the network task runner by the |channel_id_service_|,
// prior to invoking OnHandshakeIOComplete.
// Read on the NSS task runner when once OnHandshakeIOComplete is invoked
// on the NSS task runner.
const HostPortPair& host_and_port,
const SSLConfig& ssl_config,
BoundNetLog* net_log,
- ServerBoundCertService* server_bound_cert_service)
+ ChannelIDService* channel_id_service)
: detached_(false),
transport_(transport),
weak_net_log_factory_(net_log),
- server_bound_cert_service_(server_bound_cert_service),
+ channel_id_service_(channel_id_service),
unhandled_buffer_size_(0),
nss_waiting_read_(false),
nss_waiting_write_(false),
nss_is_closed_(false),
+ was_ever_used_(false),
host_and_port_(host_and_port),
ssl_config_(ssl_config),
nss_fd_(NULL),
PR_Close(nss_fd_);
nss_fd_ = NULL;
}
+ nss_bufs_ = NULL;
}
bool SSLClientSocketNSS::Core::Init(PRFileDesc* socket,
SECStatus rv = SECSuccess;
if (!ssl_config_.next_protos.empty()) {
- size_t wire_length = 0;
- for (std::vector<std::string>::const_iterator
- i = ssl_config_.next_protos.begin();
- i != ssl_config_.next_protos.end(); ++i) {
- if (i->size() > 255) {
- LOG(WARNING) << "Ignoring overlong NPN/ALPN protocol: " << *i;
- continue;
- }
- wire_length += i->size();
- wire_length++;
- }
- scoped_ptr<uint8[]> wire_protos(new uint8[wire_length]);
- uint8* dst = wire_protos.get();
- for (std::vector<std::string>::const_iterator
- i = ssl_config_.next_protos.begin();
- i != ssl_config_.next_protos.end(); i++) {
- if (i->size() > 255)
- continue;
- *dst++ = i->size();
- memcpy(dst, i->data(), i->size());
- dst += i->size();
- }
- DCHECK_EQ(dst, wire_protos.get() + wire_length);
- rv = SSL_SetNextProtoNego(nss_fd_, wire_protos.get(), wire_length);
+ std::vector<uint8_t> wire_protos =
+ SerializeNextProtos(ssl_config_.next_protos);
+ rv = SSL_SetNextProtoNego(
+ nss_fd_, wire_protos.empty() ? NULL : &wire_protos[0],
+ wire_protos.size());
if (rv != SECSuccess)
LogFailedNSSFunction(*weak_net_log_, "SSL_SetNextProtoNego", "");
rv = SSL_OptionSet(nss_fd_, SSL_ENABLE_ALPN, PR_TRUE);
return false;
}
- if (IsChannelIDEnabled(ssl_config_, server_bound_cert_service_)) {
+ if (IsChannelIDEnabled(ssl_config_, channel_id_service_)) {
rv = SSL_SetClientChannelIDCallback(
nss_fd_, SSLClientSocketNSS::Core::ClientChannelIDHandler, this);
if (rv != SECSuccess) {
return ERR_IO_PENDING;
} else {
DCHECK(!nss_waiting_read_);
- if (rv <= 0)
+ if (rv <= 0) {
nss_is_closed_ = true;
+ } else {
+ was_ever_used_ = true;
+ }
}
}
return ERR_IO_PENDING;
} else {
DCHECK(!nss_waiting_write_);
- if (rv < 0)
+ if (rv < 0) {
nss_is_closed_ = true;
+ } else if (rv > 0) {
+ was_ever_used_ = true;
+ }
}
}
return rv;
}
-bool SSLClientSocketNSS::Core::IsConnected() {
+bool SSLClientSocketNSS::Core::IsConnected() const {
DCHECK(OnNetworkTaskRunner());
return !nss_is_closed_;
}
-bool SSLClientSocketNSS::Core::HasPendingAsyncOperation() {
+bool SSLClientSocketNSS::Core::HasPendingAsyncOperation() const {
DCHECK(OnNetworkTaskRunner());
return nss_waiting_read_ || nss_waiting_write_;
}
-bool SSLClientSocketNSS::Core::HasUnhandledReceivedData() {
+bool SSLClientSocketNSS::Core::HasUnhandledReceivedData() const {
DCHECK(OnNetworkTaskRunner());
return unhandled_buffer_size_ != 0;
}
+bool SSLClientSocketNSS::Core::WasEverUsed() const {
+ DCHECK(OnNetworkTaskRunner());
+ return was_ever_used_;
+}
+
void SSLClientSocketNSS::Core::CacheSessionIfNecessary() {
// TODO(rsleevi): This should occur on the NSS task runner, due to the use of
// nss_fd_. However, it happens on the network task runner in order to match
nss_handshake_state_));
}
-int SSLClientSocketNSS::Core::HandleNSSError(PRErrorCode nss_error,
- bool handshake_error) {
+int SSLClientSocketNSS::Core::HandleNSSError(PRErrorCode nss_error) {
DCHECK(OnNSSTaskRunner());
- int net_error = handshake_error ? MapNSSClientHandshakeError(nss_error) :
- MapNSSClientError(nss_error);
+ int net_error = MapNSSClientError(nss_error);
#if defined(OS_WIN)
// On Windows, a handle to the HCRYPTPROV is cached in the X509Certificate
int SSLClientSocketNSS::Core::DoHandshake() {
DCHECK(OnNSSTaskRunner());
- int net_error = net::OK;
+ int net_error = OK;
SECStatus rv = SSL_ForceHandshake(nss_fd_);
// Note: this function may be called multiple times during the handshake, so
}
} else {
PRErrorCode prerr = PR_GetError();
- net_error = HandleNSSError(prerr, true);
-
- // Some network devices that inspect application-layer packets seem to
- // inject TCP reset packets to break the connections when they see
- // TLS 1.1 in ClientHello or ServerHello. See http://crbug.com/130293.
- //
- // Only allow ERR_CONNECTION_RESET to trigger a fallback from TLS 1.1 or
- // 1.2. We don't lose much in this fallback because the explicit IV for CBC
- // mode in TLS 1.1 is approximated by record splitting in TLS 1.0. The
- // fallback will be more painful for TLS 1.2 when we have GCM support.
- //
- // ERR_CONNECTION_RESET is a common network error, so we don't want it
- // to trigger a version fallback in general, especially the TLS 1.0 ->
- // SSL 3.0 fallback, which would drop TLS extensions.
- if (prerr == PR_CONNECT_RESET_ERROR &&
- ssl_config_.version_max >= SSL_PROTOCOL_VERSION_TLS1_1) {
- net_error = ERR_SSL_PROTOCOL_ERROR;
- }
+ net_error = HandleNSSError(prerr);
// If not done, stay in this state
if (net_error == ERR_IO_PENDING) {
// If *next_result == 0, then that indicates EOF, and no special error
// handling is needed.
pending_read_nss_error_ = PR_GetError();
- *next_result = HandleNSSError(pending_read_nss_error_, false);
+ *next_result = HandleNSSError(pending_read_nss_error_);
if (rv > 0 && *next_result == ERR_IO_PENDING) {
// If at least some data was read from PR_Read(), do not treat
// insufficient data as an error to return in the next call to
if (prerr == PR_WOULD_BLOCK_ERROR)
return ERR_IO_PENDING;
- rv = HandleNSSError(prerr, false);
+ rv = HandleNSSError(prerr);
PostOrRunCallback(
FROM_HERE,
base::Bind(&AddLogEventWithCallback, weak_net_log_,
const char* buf1;
const char* buf2;
unsigned int len1, len2;
- memio_GetWriteParams(nss_bufs_, &buf1, &len1, &buf2, &len2);
+ if (memio_GetWriteParams(nss_bufs_, &buf1, &len1, &buf2, &len2)) {
+ // It is important this return synchronously to prevent spinning infinitely
+ // in the off-thread NSS case. The error code itself is ignored, so just
+ // return ERR_ABORTED. See https://crbug.com/381160.
+ return ERR_ABORTED;
+ }
const unsigned int len = len1 + len2;
int rv = 0;
std::string host = core->host_and_port_.host();
int error = ERR_UNEXPECTED;
if (core->OnNetworkTaskRunner()) {
- error = core->DoGetDomainBoundCert(host);
+ error = core->DoGetChannelID(host);
} else {
bool posted = core->network_task_runner_->PostTask(
FROM_HERE,
base::Bind(
- IgnoreResult(&Core::DoGetDomainBoundCert),
+ IgnoreResult(&Core::DoGetChannelID),
core, host));
error = posted ? ERR_IO_PENDING : ERR_ABORTED;
}
// Set the private key.
if (!crypto::ECPrivateKey::ImportFromEncryptedPrivateKeyInfo(
slot.get(),
- ServerBoundCertService::kEPKIPassword,
+ ChannelIDService::kEPKIPassword,
reinterpret_cast<const unsigned char*>(
domain_bound_private_key_.data()),
domain_bound_private_key_.size(),
}
void SSLClientSocketNSS::Core::UpdateStapledOCSPResponse() {
+ PRBool ocsp_requested = PR_FALSE;
+ SSL_OptionGet(nss_fd_, SSL_ENABLE_OCSP_STAPLING, &ocsp_requested);
const SECItemArray* ocsp_responses =
SSL_PeerStapledOCSPResponses(nss_fd_);
- if (!ocsp_responses || !ocsp_responses->len)
+ bool ocsp_responses_present = ocsp_responses && ocsp_responses->len;
+ if (ocsp_requested)
+ UMA_HISTOGRAM_BOOLEAN("Net.OCSPResponseStapled", ocsp_responses_present);
+ if (!ocsp_responses_present)
return;
nss_handshake_state_.stapled_ocsp_response = std::string(
bool supports_ecc) const {
DCHECK(OnNetworkTaskRunner());
- RecordChannelIDSupport(server_bound_cert_service_,
+ RecordChannelIDSupport(channel_id_service_,
negotiated_channel_id,
channel_id_enabled,
supports_ecc);
return rv;
}
-int SSLClientSocketNSS::Core::DoGetDomainBoundCert(const std::string& host) {
+int SSLClientSocketNSS::Core::DoGetChannelID(const std::string& host) {
DCHECK(OnNetworkTaskRunner());
if (detached_)
- return ERR_FAILED;
+ return ERR_ABORTED;
weak_net_log_->BeginEvent(NetLog::TYPE_SSL_GET_DOMAIN_BOUND_CERT);
- int rv = server_bound_cert_service_->GetOrCreateDomainBoundCert(
+ int rv = channel_id_service_->GetOrCreateChannelID(
host,
&domain_bound_private_key_,
&domain_bound_cert_,
- base::Bind(&Core::OnGetDomainBoundCertComplete, base::Unretained(this)),
+ base::Bind(&Core::OnGetChannelIDComplete, base::Unretained(this)),
&domain_bound_cert_request_handle_);
if (rv != ERR_IO_PENDING && !OnNSSTaskRunner()) {
DCHECK(OnNetworkTaskRunner());
DCHECK(nss_waiting_read_);
nss_waiting_read_ = false;
- if (result <= 0)
+ if (result <= 0) {
nss_is_closed_ = true;
+ } else {
+ was_ever_used_ = true;
+ }
}
void SSLClientSocketNSS::Core::DidNSSWrite(int result) {
DCHECK(OnNetworkTaskRunner());
DCHECK(nss_waiting_write_);
nss_waiting_write_ = false;
- if (result < 0)
+ if (result < 0) {
nss_is_closed_ = true;
+ } else if (result > 0) {
+ was_ever_used_ = true;
+ }
}
void SSLClientSocketNSS::Core::BufferSendComplete(int result) {
DoConnectCallback(rv);
}
-void SSLClientSocketNSS::Core::OnGetDomainBoundCertComplete(int result) {
+void SSLClientSocketNSS::Core::OnGetChannelIDComplete(int result) {
DVLOG(1) << __FUNCTION__ << " " << result;
DCHECK(OnNetworkTaskRunner());
ssl_config_(ssl_config),
cert_verifier_(context.cert_verifier),
cert_transparency_verifier_(context.cert_transparency_verifier),
- server_bound_cert_service_(context.server_bound_cert_service),
+ channel_id_service_(context.channel_id_service),
ssl_session_cache_shard_(context.ssl_session_cache_shard),
completed_handshake_(false),
next_handshake_state_(STATE_NONE),
ssl_info->client_cert_sent =
ssl_config_.send_client_cert && ssl_config_.client_cert.get();
ssl_info->channel_id_sent = WasChannelIDSent();
+ ssl_info->pinning_failure_log = pinning_failure_log_;
PRUint16 cipher_suite = SSLConnectionStatusToCipherSuite(
core_->state().ssl_connection_status);
return true;
}
+bool SSLClientSocketNSS::InSessionCache() const {
+ // For now, always return true so that SSLConnectJobs are never held back.
+ return true;
+}
+
+void SSLClientSocketNSS::SetHandshakeCompletionCallback(
+ const base::Closure& callback) {
+ NOTIMPLEMENTED();
+}
+
void SSLClientSocketNSS::GetSSLCertRequestInfo(
SSLCertRequestInfo* cert_request_info) {
EnterFunction("");
}
SSLClientSocket::NextProtoStatus
-SSLClientSocketNSS::GetNextProto(std::string* proto,
- std::string* server_protos) {
+SSLClientSocketNSS::GetNextProto(std::string* proto) {
*proto = core_->state().next_proto;
- *server_protos = core_->state().server_protos;
return core_->state().next_proto_status;
}
}
bool SSLClientSocketNSS::WasEverUsed() const {
- if (transport_.get() && transport_->socket()) {
- return transport_->socket()->WasEverUsed();
- }
- NOTREACHED();
- return false;
+ DCHECK(core_.get());
+
+ return core_->WasEverUsed();
}
bool SSLClientSocketNSS::UsingTCPFastOpen() const {
return rv;
}
-bool SSLClientSocketNSS::SetReceiveBufferSize(int32 size) {
+int SSLClientSocketNSS::SetReceiveBufferSize(int32 size) {
return transport_->socket()->SetReceiveBufferSize(size);
}
-bool SSLClientSocketNSS::SetSendBufferSize(int32 size) {
+int SSLClientSocketNSS::SetSendBufferSize(int32 size) {
return transport_->socket()->SetSendBufferSize(size);
}
host_and_port_,
ssl_config_,
&net_log_,
- server_bound_cert_service_);
+ channel_id_service_);
}
int SSLClientSocketNSS::InitializeSSLOptions() {
SockaddrStorage storage;
if (!peer_address.ToSockAddr(storage.addr, &storage.addr_len))
- return ERR_UNEXPECTED;
+ return ERR_ADDRESS_INVALID;
PRNetAddr peername;
memset(&peername, 0, sizeof(peername));
if (result == OK)
LogConnectionTypeMetrics();
-#if defined(OFFICIAL_BUILD) && !defined(OS_ANDROID) && !defined(OS_IOS)
- // Take care of any mandates for public key pinning.
- //
- // Pinning is only enabled for official builds to make sure that others don't
- // end up with pins that cannot be easily updated.
- //
- // TODO(agl): We might have an issue here where a request for foo.example.com
- // merges into a SPDY connection to www.example.com, and gets a different
- // certificate.
-
- // Perform pin validation if, and only if, all these conditions obtain:
- //
- // * a TransportSecurityState object is available;
- // * the server's certificate chain is valid (or suffers from only a minor
- // error);
- // * the server's certificate chain chains up to a known root (i.e. not a
- // user-installed trust anchor); and
- // * the build is recent (very old builds should fail open so that users
- // have some chance to recover).
- //
+ bool sni_available = ssl_config_.version_max >= SSL_PROTOCOL_VERSION_TLS1 ||
+ ssl_config_.version_fallback;
const CertStatus cert_status = server_cert_verify_result_.cert_status;
if (transport_security_state_ &&
(result == OK ||
(IsCertificateError(result) && IsCertStatusMinorError(cert_status))) &&
- server_cert_verify_result_.is_issued_by_known_root &&
- TransportSecurityState::IsBuildTimely()) {
- bool sni_available =
- ssl_config_.version_max >= SSL_PROTOCOL_VERSION_TLS1 ||
- ssl_config_.version_fallback;
- const std::string& host = host_and_port_.host();
-
- TransportSecurityState::DomainState domain_state;
- if (transport_security_state_->GetDomainState(host, sni_available,
- &domain_state) &&
- domain_state.HasPublicKeyPins()) {
- if (!domain_state.CheckPublicKeyPins(
- server_cert_verify_result_.public_key_hashes)) {
- result = ERR_SSL_PINNED_KEY_NOT_IN_CERT_CHAIN;
- UMA_HISTOGRAM_BOOLEAN("Net.PublicKeyPinSuccess", false);
- TransportSecurityState::ReportUMAOnPinFailure(host);
- } else {
- UMA_HISTOGRAM_BOOLEAN("Net.PublicKeyPinSuccess", true);
- }
- }
+ !transport_security_state_->CheckPublicKeyPins(
+ host_and_port_.host(),
+ sni_available,
+ server_cert_verify_result_.is_issued_by_known_root,
+ server_cert_verify_result_.public_key_hashes,
+ &pinning_failure_log_)) {
+ result = ERR_SSL_PINNED_KEY_NOT_IN_CERT_CHAIN;
}
-#endif
if (result == OK) {
// Only check Certificate Transparency if there were no other errors with
}
}
-ServerBoundCertService* SSLClientSocketNSS::GetServerBoundCertService() const {
- return server_bound_cert_service_;
+scoped_refptr<X509Certificate>
+SSLClientSocketNSS::GetUnverifiedServerCertificateChain() const {
+ return core_->state().server_cert.get();
+}
+
+ChannelIDService* SSLClientSocketNSS::GetChannelIDService() const {
+ return channel_id_service_;
}
} // namespace net