diff options
author | rsleevi@chromium.org <rsleevi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-11-09 04:32:22 +0000 |
---|---|---|
committer | rsleevi@chromium.org <rsleevi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-11-09 04:32:22 +0000 |
commit | b9b651f0e2f8d5ef82743af2c42180112613c05c (patch) | |
tree | e12e0feca2d04055a962a69f7036de20236f49c4 /net/socket | |
parent | 82591ee1bfca43a04e986eec374af9764279c17a (diff) | |
download | chromium_src-b9b651f0e2f8d5ef82743af2c42180112613c05c.zip chromium_src-b9b651f0e2f8d5ef82743af2c42180112613c05c.tar.gz chromium_src-b9b651f0e2f8d5ef82743af2c42180112613c05c.tar.bz2 |
Style: Clean-up SSLClientSocketOpenSSL to match header declaration
This aligns SSLClientSocketOpenSSL with the rest of the net/ work that
was done in http://crrev.com/71017 and http://crrev.com/52528. While
cosmetic in nature, it is consistent with the rest of net/
BUG=none
R=agl
Review URL: https://codereview.chromium.org/65393002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@234090 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/socket')
-rw-r--r-- | net/socket/ssl_client_socket_openssl.cc | 1173 |
1 files changed, 584 insertions, 589 deletions
diff --git a/net/socket/ssl_client_socket_openssl.cc b/net/socket/ssl_client_socket_openssl.cc index a53eb52..1415075 100644 --- a/net/socket/ssl_client_socket_openssl.cc +++ b/net/socket/ssl_client_socket_openssl.cc @@ -465,6 +465,279 @@ SSLClientSocketOpenSSL::~SSLClientSocketOpenSSL() { Disconnect(); } +void SSLClientSocketOpenSSL::GetSSLCertRequestInfo( + SSLCertRequestInfo* cert_request_info) { + cert_request_info->host_and_port = host_and_port_.ToString(); + cert_request_info->cert_authorities = cert_authorities_; +} + +SSLClientSocket::NextProtoStatus SSLClientSocketOpenSSL::GetNextProto( + std::string* proto, std::string* server_protos) { + *proto = npn_proto_; + *server_protos = server_protos_; + return npn_status_; +} + +ServerBoundCertService* +SSLClientSocketOpenSSL::GetServerBoundCertService() const { + return server_bound_cert_service_; +} + +int SSLClientSocketOpenSSL::ExportKeyingMaterial( + const base::StringPiece& label, + bool has_context, const base::StringPiece& context, + unsigned char* out, unsigned int outlen) { + crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); + + int rv = SSL_export_keying_material( + ssl_, out, outlen, const_cast<char*>(label.data()), + label.size(), + reinterpret_cast<unsigned char*>(const_cast<char*>(context.data())), + context.length(), + context.length() > 0); + + if (rv != 1) { + int ssl_error = SSL_get_error(ssl_, rv); + LOG(ERROR) << "Failed to export keying material;" + << " returned " << rv + << ", SSL error code " << ssl_error; + return MapOpenSSLError(ssl_error, err_tracer); + } + return OK; +} + +int SSLClientSocketOpenSSL::GetTLSUniqueChannelBinding(std::string* out) { + return ERR_NOT_IMPLEMENTED; +} + +int SSLClientSocketOpenSSL::Connect(const CompletionCallback& callback) { + net_log_.BeginEvent(NetLog::TYPE_SSL_CONNECT); + + // Set up new ssl object. + if (!Init()) { + int result = ERR_UNEXPECTED; + net_log_.EndEventWithNetErrorCode(NetLog::TYPE_SSL_CONNECT, result); + return result; + } + + // Set SSL to client mode. Handshake happens in the loop below. + SSL_set_connect_state(ssl_); + + GotoState(STATE_HANDSHAKE); + int rv = DoHandshakeLoop(net::OK); + if (rv == ERR_IO_PENDING) { + user_connect_callback_ = callback; + } else { + net_log_.EndEventWithNetErrorCode(NetLog::TYPE_SSL_CONNECT, rv); + } + + return rv > OK ? OK : rv; +} + +void SSLClientSocketOpenSSL::Disconnect() { + if (ssl_) { + // Calling SSL_shutdown prevents the session from being marked as + // unresumable. + SSL_shutdown(ssl_); + SSL_free(ssl_); + ssl_ = NULL; + } + if (transport_bio_) { + BIO_free_all(transport_bio_); + transport_bio_ = NULL; + } + + // Shut down anything that may call us back. + verifier_.reset(); + transport_->socket()->Disconnect(); + + // Null all callbacks, delete all buffers. + transport_send_busy_ = false; + send_buffer_ = NULL; + transport_recv_busy_ = false; + transport_recv_eof_ = false; + recv_buffer_ = NULL; + + user_connect_callback_.Reset(); + user_read_callback_.Reset(); + user_write_callback_.Reset(); + user_read_buf_ = NULL; + user_read_buf_len_ = 0; + user_write_buf_ = NULL; + user_write_buf_len_ = 0; + + server_cert_verify_result_.Reset(); + completed_handshake_ = false; + + cert_authorities_.clear(); + client_auth_cert_needed_ = false; +} + +bool SSLClientSocketOpenSSL::IsConnected() const { + // If the handshake has not yet completed. + if (!completed_handshake_) + return false; + // If an asynchronous operation is still pending. + if (user_read_buf_.get() || user_write_buf_.get()) + return true; + + return transport_->socket()->IsConnected(); +} + +bool SSLClientSocketOpenSSL::IsConnectedAndIdle() const { + // If the handshake has not yet completed. + if (!completed_handshake_) + return false; + // If an asynchronous operation is still pending. + if (user_read_buf_.get() || user_write_buf_.get()) + return false; + // If there is data waiting to be sent, or data read from the network that + // has not yet been consumed. + if (BIO_ctrl_pending(transport_bio_) > 0 || + BIO_ctrl_wpending(transport_bio_) > 0) { + return false; + } + + return transport_->socket()->IsConnectedAndIdle(); +} + +int SSLClientSocketOpenSSL::GetPeerAddress(IPEndPoint* addressList) const { + return transport_->socket()->GetPeerAddress(addressList); +} + +int SSLClientSocketOpenSSL::GetLocalAddress(IPEndPoint* addressList) const { + return transport_->socket()->GetLocalAddress(addressList); +} + +const BoundNetLog& SSLClientSocketOpenSSL::NetLog() const { + return net_log_; +} + +void SSLClientSocketOpenSSL::SetSubresourceSpeculation() { + if (transport_.get() && transport_->socket()) { + transport_->socket()->SetSubresourceSpeculation(); + } else { + NOTREACHED(); + } +} + +void SSLClientSocketOpenSSL::SetOmniboxSpeculation() { + if (transport_.get() && transport_->socket()) { + transport_->socket()->SetOmniboxSpeculation(); + } else { + NOTREACHED(); + } +} + +bool SSLClientSocketOpenSSL::WasEverUsed() const { + if (transport_.get() && transport_->socket()) + return transport_->socket()->WasEverUsed(); + + NOTREACHED(); + return false; +} + +bool SSLClientSocketOpenSSL::UsingTCPFastOpen() const { + if (transport_.get() && transport_->socket()) + return transport_->socket()->UsingTCPFastOpen(); + + NOTREACHED(); + return false; +} + +bool SSLClientSocketOpenSSL::GetSSLInfo(SSLInfo* ssl_info) { + ssl_info->Reset(); + if (!server_cert_.get()) + return false; + + ssl_info->cert = server_cert_verify_result_.verified_cert; + ssl_info->cert_status = server_cert_verify_result_.cert_status; + ssl_info->is_issued_by_known_root = + server_cert_verify_result_.is_issued_by_known_root; + ssl_info->public_key_hashes = + server_cert_verify_result_.public_key_hashes; + ssl_info->client_cert_sent = + ssl_config_.send_client_cert && ssl_config_.client_cert.get(); + ssl_info->channel_id_sent = WasChannelIDSent(); + + RecordChannelIDSupport(server_bound_cert_service_, + channel_id_xtn_negotiated_, + ssl_config_.channel_id_enabled, + crypto::ECPrivateKey::IsSupported()); + + const SSL_CIPHER* cipher = SSL_get_current_cipher(ssl_); + CHECK(cipher); + ssl_info->security_bits = SSL_CIPHER_get_bits(cipher, NULL); + const COMP_METHOD* compression = SSL_get_current_compression(ssl_); + + ssl_info->connection_status = EncodeSSLConnectionStatus( + SSL_CIPHER_get_id(cipher), + compression ? compression->type : 0, + GetNetSSLVersion(ssl_)); + + bool peer_supports_renego_ext = !!SSL_get_secure_renegotiation_support(ssl_); + if (!peer_supports_renego_ext) + ssl_info->connection_status |= SSL_CONNECTION_NO_RENEGOTIATION_EXTENSION; + UMA_HISTOGRAM_ENUMERATION("Net.RenegotiationExtensionSupported", + implicit_cast<int>(peer_supports_renego_ext), 2); + + if (ssl_config_.version_fallback) + ssl_info->connection_status |= SSL_CONNECTION_VERSION_FALLBACK; + + ssl_info->handshake_type = SSL_session_reused(ssl_) ? + SSLInfo::HANDSHAKE_RESUME : SSLInfo::HANDSHAKE_FULL; + + DVLOG(3) << "Encoded connection status: cipher suite = " + << SSLConnectionStatusToCipherSuite(ssl_info->connection_status) + << " version = " + << SSLConnectionStatusToVersion(ssl_info->connection_status); + return true; +} + +int SSLClientSocketOpenSSL::Read(IOBuffer* buf, + int buf_len, + const CompletionCallback& callback) { + user_read_buf_ = buf; + user_read_buf_len_ = buf_len; + + int rv = DoReadLoop(OK); + + if (rv == ERR_IO_PENDING) { + user_read_callback_ = callback; + } else { + user_read_buf_ = NULL; + user_read_buf_len_ = 0; + } + + return rv; +} + +int SSLClientSocketOpenSSL::Write(IOBuffer* buf, + int buf_len, + const CompletionCallback& callback) { + user_write_buf_ = buf; + user_write_buf_len_ = buf_len; + + int rv = DoWriteLoop(OK); + + if (rv == ERR_IO_PENDING) { + user_write_callback_ = callback; + } else { + user_write_buf_ = NULL; + user_write_buf_len_ = 0; + } + + return rv; +} + +bool SSLClientSocketOpenSSL::SetReceiveBufferSize(int32 size) { + return transport_->socket()->SetReceiveBufferSize(size); +} + +bool SSLClientSocketOpenSSL::SetSendBufferSize(int32 size) { + return transport_->socket()->SetSendBufferSize(size); +} + bool SSLClientSocketOpenSSL::Init() { DCHECK(!ssl_); DCHECK(!transport_bio_); @@ -587,189 +860,6 @@ bool SSLClientSocketOpenSSL::Init() { return true; } -int SSLClientSocketOpenSSL::ClientCertRequestCallback(SSL* ssl, - X509** x509, - EVP_PKEY** pkey) { - DVLOG(3) << "OpenSSL ClientCertRequestCallback called"; - DCHECK(ssl == ssl_); - DCHECK(*x509 == NULL); - DCHECK(*pkey == NULL); - - if (!ssl_config_.send_client_cert) { - // First pass: we know that a client certificate is needed, but we do not - // have one at hand. - client_auth_cert_needed_ = true; - STACK_OF(X509_NAME) *authorities = SSL_get_client_CA_list(ssl); - for (int i = 0; i < sk_X509_NAME_num(authorities); i++) { - X509_NAME *ca_name = (X509_NAME *)sk_X509_NAME_value(authorities, i); - unsigned char* str = NULL; - int length = i2d_X509_NAME(ca_name, &str); - cert_authorities_.push_back(std::string( - reinterpret_cast<const char*>(str), - static_cast<size_t>(length))); - OPENSSL_free(str); - } - - return -1; // Suspends handshake. - } - - // Second pass: a client certificate should have been selected. - if (ssl_config_.client_cert.get()) { - // A note about ownership: FetchClientCertPrivateKey() increments - // the reference count of the EVP_PKEY. Ownership of this reference - // is passed directly to OpenSSL, which will release the reference - // using EVP_PKEY_free() when the SSL object is destroyed. - OpenSSLClientKeyStore::ScopedEVP_PKEY privkey; - if (OpenSSLClientKeyStore::GetInstance()->FetchClientCertPrivateKey( - ssl_config_.client_cert.get(), &privkey)) { - // TODO(joth): (copied from NSS) We should wait for server certificate - // verification before sending our credentials. See http://crbug.com/13934 - *x509 = X509Certificate::DupOSCertHandle( - ssl_config_.client_cert->os_cert_handle()); - *pkey = privkey.release(); - return 1; - } - LOG(WARNING) << "Client cert found without private key"; - } - - // Send no client certificate. - return 0; -} - -void SSLClientSocketOpenSSL::ChannelIDRequestCallback(SSL* ssl, - EVP_PKEY** pkey) { - DVLOG(3) << "OpenSSL ChannelIDRequestCallback called"; - DCHECK_EQ(ssl, ssl_); - DCHECK(!*pkey); - - channel_id_xtn_negotiated_ = true; - if (!channel_id_private_key_.size()) { - channel_id_request_return_value_ = - server_bound_cert_service_->GetOrCreateDomainBoundCert( - host_and_port_.host(), - &channel_id_private_key_, - &channel_id_cert_, - base::Bind(&SSLClientSocketOpenSSL::OnHandshakeIOComplete, - base::Unretained(this)), - &channel_id_request_handle_); - if (channel_id_request_return_value_ != OK) - return; - } - - // Decode key. - std::vector<uint8> encrypted_private_key_info; - std::vector<uint8> subject_public_key_info; - encrypted_private_key_info.assign( - channel_id_private_key_.data(), - channel_id_private_key_.data() + channel_id_private_key_.size()); - subject_public_key_info.assign( - channel_id_cert_.data(), - channel_id_cert_.data() + channel_id_cert_.size()); - scoped_ptr<crypto::ECPrivateKey> ec_private_key( - crypto::ECPrivateKey::CreateFromEncryptedPrivateKeyInfo( - ServerBoundCertService::kEPKIPassword, - encrypted_private_key_info, - subject_public_key_info)); - set_channel_id_sent(true); - *pkey = EVP_PKEY_dup(ec_private_key->key()); -} - -// SSLClientSocket methods - -bool SSLClientSocketOpenSSL::GetSSLInfo(SSLInfo* ssl_info) { - ssl_info->Reset(); - if (!server_cert_.get()) - return false; - - ssl_info->cert = server_cert_verify_result_.verified_cert; - ssl_info->cert_status = server_cert_verify_result_.cert_status; - ssl_info->is_issued_by_known_root = - server_cert_verify_result_.is_issued_by_known_root; - ssl_info->public_key_hashes = - server_cert_verify_result_.public_key_hashes; - ssl_info->client_cert_sent = - ssl_config_.send_client_cert && ssl_config_.client_cert.get(); - ssl_info->channel_id_sent = WasChannelIDSent(); - - RecordChannelIDSupport(server_bound_cert_service_, - channel_id_xtn_negotiated_, - ssl_config_.channel_id_enabled, - crypto::ECPrivateKey::IsSupported()); - - const SSL_CIPHER* cipher = SSL_get_current_cipher(ssl_); - CHECK(cipher); - ssl_info->security_bits = SSL_CIPHER_get_bits(cipher, NULL); - const COMP_METHOD* compression = SSL_get_current_compression(ssl_); - - ssl_info->connection_status = EncodeSSLConnectionStatus( - SSL_CIPHER_get_id(cipher), - compression ? compression->type : 0, - GetNetSSLVersion(ssl_)); - - bool peer_supports_renego_ext = !!SSL_get_secure_renegotiation_support(ssl_); - if (!peer_supports_renego_ext) - ssl_info->connection_status |= SSL_CONNECTION_NO_RENEGOTIATION_EXTENSION; - UMA_HISTOGRAM_ENUMERATION("Net.RenegotiationExtensionSupported", - implicit_cast<int>(peer_supports_renego_ext), 2); - - if (ssl_config_.version_fallback) - ssl_info->connection_status |= SSL_CONNECTION_VERSION_FALLBACK; - - ssl_info->handshake_type = SSL_session_reused(ssl_) ? - SSLInfo::HANDSHAKE_RESUME : SSLInfo::HANDSHAKE_FULL; - - DVLOG(3) << "Encoded connection status: cipher suite = " - << SSLConnectionStatusToCipherSuite(ssl_info->connection_status) - << " version = " - << SSLConnectionStatusToVersion(ssl_info->connection_status); - return true; -} - -void SSLClientSocketOpenSSL::GetSSLCertRequestInfo( - SSLCertRequestInfo* cert_request_info) { - cert_request_info->host_and_port = host_and_port_.ToString(); - cert_request_info->cert_authorities = cert_authorities_; -} - -int SSLClientSocketOpenSSL::ExportKeyingMaterial( - const base::StringPiece& label, - bool has_context, const base::StringPiece& context, - unsigned char* out, unsigned int outlen) { - crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); - - int rv = SSL_export_keying_material( - ssl_, out, outlen, const_cast<char*>(label.data()), - label.size(), - reinterpret_cast<unsigned char*>(const_cast<char*>(context.data())), - context.length(), - context.length() > 0); - - if (rv != 1) { - int ssl_error = SSL_get_error(ssl_, rv); - LOG(ERROR) << "Failed to export keying material;" - << " returned " << rv - << ", SSL error code " << ssl_error; - return MapOpenSSLError(ssl_error, err_tracer); - } - return OK; -} - -int SSLClientSocketOpenSSL::GetTLSUniqueChannelBinding(std::string* out) { - return ERR_NOT_IMPLEMENTED; -} - -SSLClientSocket::NextProtoStatus SSLClientSocketOpenSSL::GetNextProto( - std::string* proto, std::string* server_protos) { - *proto = npn_proto_; - *server_protos = server_protos_; - return npn_status_; -} - -ServerBoundCertService* -SSLClientSocketOpenSSL::GetServerBoundCertService() const { - return server_bound_cert_service_; -} - void SSLClientSocketOpenSSL::DoReadCallback(int rv) { // Since Run may result in Read being called, clear |user_read_callback_| // up front. @@ -786,107 +876,19 @@ void SSLClientSocketOpenSSL::DoWriteCallback(int rv) { base::ResetAndReturn(&user_write_callback_).Run(rv); } -// StreamSocket implementation. -int SSLClientSocketOpenSSL::Connect(const CompletionCallback& callback) { - net_log_.BeginEvent(NetLog::TYPE_SSL_CONNECT); - - // Set up new ssl object. - if (!Init()) { - int result = ERR_UNEXPECTED; - net_log_.EndEventWithNetErrorCode(NetLog::TYPE_SSL_CONNECT, result); - return result; - } - - // Set SSL to client mode. Handshake happens in the loop below. - SSL_set_connect_state(ssl_); - - GotoState(STATE_HANDSHAKE); - int rv = DoHandshakeLoop(net::OK); - if (rv == ERR_IO_PENDING) { - user_connect_callback_ = callback; - } else { - net_log_.EndEventWithNetErrorCode(NetLog::TYPE_SSL_CONNECT, rv); - } - - return rv > OK ? OK : rv; -} - -void SSLClientSocketOpenSSL::Disconnect() { - if (ssl_) { - // Calling SSL_shutdown prevents the session from being marked as - // unresumable. - SSL_shutdown(ssl_); - SSL_free(ssl_); - ssl_ = NULL; - } - if (transport_bio_) { - BIO_free_all(transport_bio_); - transport_bio_ = NULL; - } - - // Shut down anything that may call us back. - verifier_.reset(); - transport_->socket()->Disconnect(); - - // Null all callbacks, delete all buffers. - transport_send_busy_ = false; - send_buffer_ = NULL; - transport_recv_busy_ = false; - transport_recv_eof_ = false; - recv_buffer_ = NULL; - - user_connect_callback_.Reset(); - user_read_callback_.Reset(); - user_write_callback_.Reset(); - user_read_buf_ = NULL; - user_read_buf_len_ = 0; - user_write_buf_ = NULL; - user_write_buf_len_ = 0; - - server_cert_verify_result_.Reset(); - completed_handshake_ = false; - - cert_authorities_.clear(); - client_auth_cert_needed_ = false; -} - -int SSLClientSocketOpenSSL::DoHandshakeLoop(int last_io_result) { - int rv = last_io_result; +bool SSLClientSocketOpenSSL::DoTransportIO() { + bool network_moved = false; + int rv; + // Read and write as much data as possible. The loop is necessary because + // Write() may return synchronously. do { - // Default to STATE_NONE for next state. - // (This is a quirk carried over from the windows - // implementation. It makes reading the logs a bit harder.) - // State handlers can and often do call GotoState just - // to stay in the current state. - State state = next_handshake_state_; - GotoState(STATE_NONE); - switch (state) { - case STATE_HANDSHAKE: - rv = DoHandshake(); - break; - case STATE_VERIFY_CERT: - DCHECK(rv == OK); - rv = DoVerifyCert(rv); - break; - case STATE_VERIFY_CERT_COMPLETE: - rv = DoVerifyCertComplete(rv); - break; - case STATE_NONE: - default: - rv = ERR_UNEXPECTED; - NOTREACHED() << "unexpected state" << state; - break; - } - - bool network_moved = DoTransportIO(); - if (network_moved && next_handshake_state_ == STATE_HANDSHAKE) { - // In general we exit the loop if rv is ERR_IO_PENDING. In this - // special case we keep looping even if rv is ERR_IO_PENDING because - // the transport IO may allow DoHandshake to make progress. - rv = OK; // This causes us to stay in the loop. - } - } while (rv != ERR_IO_PENDING && next_handshake_state_ != STATE_NONE); - return rv; + rv = BufferSend(); + if (rv != ERR_IO_PENDING && rv != 0) + network_moved = true; + } while (rv > 0); + if (!transport_recv_eof_ && BufferRecv() != ERR_IO_PENDING) + network_moved = true; + return network_moved; } int SSLClientSocketOpenSSL::DoHandshake() { @@ -948,58 +950,6 @@ int SSLClientSocketOpenSSL::DoHandshake() { return net_error; } -// SelectNextProtoCallback is called by OpenSSL during the handshake. If the -// server supports NPN, selects a protocol from the list that the server -// provides. According to third_party/openssl/openssl/ssl/ssl_lib.c, the -// callback can assume that |in| is syntactically valid. -int SSLClientSocketOpenSSL::SelectNextProtoCallback(unsigned char** out, - unsigned char* outlen, - const unsigned char* in, - unsigned int inlen) { -#if defined(OPENSSL_NPN_NEGOTIATED) - if (ssl_config_.next_protos.empty()) { - *out = reinterpret_cast<uint8*>( - const_cast<char*>(kDefaultSupportedNPNProtocol)); - *outlen = arraysize(kDefaultSupportedNPNProtocol) - 1; - npn_status_ = kNextProtoUnsupported; - return SSL_TLSEXT_ERR_OK; - } - - // Assume there's no overlap between our protocols and the server's list. - npn_status_ = kNextProtoNoOverlap; - - // For each protocol in server preference order, see if we support it. - for (unsigned int i = 0; i < inlen; i += in[i] + 1) { - for (std::vector<std::string>::const_iterator - j = ssl_config_.next_protos.begin(); - j != ssl_config_.next_protos.end(); ++j) { - if (in[i] == j->size() && - memcmp(&in[i + 1], j->data(), in[i]) == 0) { - // We found a match. - *out = const_cast<unsigned char*>(in) + i + 1; - *outlen = in[i]; - npn_status_ = kNextProtoNegotiated; - break; - } - } - if (npn_status_ == kNextProtoNegotiated) - break; - } - - // If we didn't find a protocol, we select the first one from our list. - if (npn_status_ == kNextProtoNoOverlap) { - *out = reinterpret_cast<uint8*>(const_cast<char*>( - ssl_config_.next_protos[0].data())); - *outlen = ssl_config_.next_protos[0].size(); - } - - npn_proto_.assign(reinterpret_cast<const char*>(*out), *outlen); - server_protos_.assign(reinterpret_cast<const char*>(in), inlen); - DVLOG(2) << "next protocol: '" << npn_proto_ << "' status: " << npn_status_; -#endif - return SSL_TLSEXT_ERR_OK; -} - int SSLClientSocketOpenSSL::DoVerifyCert(int result) { DCHECK(server_cert_.get()); GotoState(STATE_VERIFY_CERT_COMPLETE); @@ -1051,6 +1001,14 @@ int SSLClientSocketOpenSSL::DoVerifyCertComplete(int result) { return result; } +void SSLClientSocketOpenSSL::DoConnectCallback(int rv) { + if (!user_connect_callback_.is_null()) { + CompletionCallback c = user_connect_callback_; + user_connect_callback_.Reset(); + c.Run(rv > OK ? OK : rv); + } +} + X509Certificate* SSLClientSocketOpenSSL::UpdateServerCert() { if (server_cert_.get()) return server_cert_.get(); @@ -1075,146 +1033,6 @@ X509Certificate* SSLClientSocketOpenSSL::UpdateServerCert() { return server_cert_.get(); } -bool SSLClientSocketOpenSSL::DoTransportIO() { - bool network_moved = false; - int rv; - // Read and write as much data as possible. The loop is necessary because - // Write() may return synchronously. - do { - rv = BufferSend(); - if (rv != ERR_IO_PENDING && rv != 0) - network_moved = true; - } while (rv > 0); - if (!transport_recv_eof_ && BufferRecv() != ERR_IO_PENDING) - network_moved = true; - return network_moved; -} - -int SSLClientSocketOpenSSL::BufferSend(void) { - if (transport_send_busy_) - return ERR_IO_PENDING; - - if (!send_buffer_.get()) { - // Get a fresh send buffer out of the send BIO. - size_t max_read = BIO_ctrl_pending(transport_bio_); - if (!max_read) - return 0; // Nothing pending in the OpenSSL write BIO. - send_buffer_ = new DrainableIOBuffer(new IOBuffer(max_read), max_read); - int read_bytes = BIO_read(transport_bio_, send_buffer_->data(), max_read); - DCHECK_GT(read_bytes, 0); - CHECK_EQ(static_cast<int>(max_read), read_bytes); - } - - int rv = transport_->socket()->Write( - send_buffer_.get(), - send_buffer_->BytesRemaining(), - base::Bind(&SSLClientSocketOpenSSL::BufferSendComplete, - base::Unretained(this))); - if (rv == ERR_IO_PENDING) { - transport_send_busy_ = true; - } else { - TransportWriteComplete(rv); - } - return rv; -} - -void SSLClientSocketOpenSSL::BufferSendComplete(int result) { - transport_send_busy_ = false; - TransportWriteComplete(result); - OnSendComplete(result); -} - -void SSLClientSocketOpenSSL::TransportWriteComplete(int result) { - DCHECK(ERR_IO_PENDING != result); - if (result < 0) { - // Got a socket write error; close the BIO to indicate this upward. - DVLOG(1) << "TransportWriteComplete error " << result; - (void)BIO_shutdown_wr(transport_bio_); - BIO_set_mem_eof_return(transport_bio_, 0); - send_buffer_ = NULL; - } else { - DCHECK(send_buffer_.get()); - send_buffer_->DidConsume(result); - DCHECK_GE(send_buffer_->BytesRemaining(), 0); - if (send_buffer_->BytesRemaining() <= 0) - send_buffer_ = NULL; - } -} - -int SSLClientSocketOpenSSL::BufferRecv(void) { - if (transport_recv_busy_) - return ERR_IO_PENDING; - - // Determine how much was requested from |transport_bio_| that was not - // actually available. - size_t requested = BIO_ctrl_get_read_request(transport_bio_); - if (requested == 0) { - // This is not a perfect match of error codes, as no operation is - // actually pending. However, returning 0 would be interpreted as - // a possible sign of EOF, which is also an inappropriate match. - return ERR_IO_PENDING; - } - - // Known Issue: While only reading |requested| data is the more correct - // implementation, it has the downside of resulting in frequent reads: - // One read for the SSL record header (~5 bytes) and one read for the SSL - // record body. Rather than issuing these reads to the underlying socket - // (and constantly allocating new IOBuffers), a single Read() request to - // fill |transport_bio_| is issued. As long as an SSL client socket cannot - // be gracefully shutdown (via SSL close alerts) and re-used for non-SSL - // traffic, this over-subscribed Read()ing will not cause issues. - size_t max_write = BIO_ctrl_get_write_guarantee(transport_bio_); - if (!max_write) - return ERR_IO_PENDING; - - recv_buffer_ = new IOBuffer(max_write); - int rv = transport_->socket()->Read( - recv_buffer_.get(), - max_write, - base::Bind(&SSLClientSocketOpenSSL::BufferRecvComplete, - base::Unretained(this))); - if (rv == ERR_IO_PENDING) { - transport_recv_busy_ = true; - } else { - TransportReadComplete(rv); - } - return rv; -} - -void SSLClientSocketOpenSSL::BufferRecvComplete(int result) { - TransportReadComplete(result); - OnRecvComplete(result); -} - -void SSLClientSocketOpenSSL::TransportReadComplete(int result) { - DCHECK(ERR_IO_PENDING != result); - if (result <= 0) { - DVLOG(1) << "TransportReadComplete result " << result; - // Received 0 (end of file) or an error. Either way, bubble it up to the - // SSL layer via the BIO. TODO(joth): consider stashing the error code, to - // relay up to the SSL socket client (i.e. via DoReadCallback). - if (result == 0) - transport_recv_eof_ = true; - BIO_set_mem_eof_return(transport_bio_, 0); - (void)BIO_shutdown_wr(transport_bio_); - } else { - DCHECK(recv_buffer_.get()); - int ret = BIO_write(transport_bio_, recv_buffer_->data(), result); - // A write into a memory BIO should always succeed. - CHECK_EQ(result, ret); - } - recv_buffer_ = NULL; - transport_recv_busy_ = false; -} - -void SSLClientSocketOpenSSL::DoConnectCallback(int rv) { - if (!user_connect_callback_.is_null()) { - CompletionCallback c = user_connect_callback_; - user_connect_callback_.Reset(); - c.Run(rv > OK ? OK : rv); - } -} - void SSLClientSocketOpenSSL::OnHandshakeIOComplete(int result) { int rv = DoHandshakeLoop(result); if (rv != ERR_IO_PENDING) { @@ -1275,95 +1093,42 @@ void SSLClientSocketOpenSSL::OnRecvComplete(int result) { DoReadCallback(rv); } -bool SSLClientSocketOpenSSL::IsConnected() const { - // If the handshake has not yet completed. - if (!completed_handshake_) - return false; - // If an asynchronous operation is still pending. - if (user_read_buf_.get() || user_write_buf_.get()) - return true; - - return transport_->socket()->IsConnected(); -} - -bool SSLClientSocketOpenSSL::IsConnectedAndIdle() const { - // If the handshake has not yet completed. - if (!completed_handshake_) - return false; - // If an asynchronous operation is still pending. - if (user_read_buf_.get() || user_write_buf_.get()) - return false; - // If there is data waiting to be sent, or data read from the network that - // has not yet been consumed. - if (BIO_ctrl_pending(transport_bio_) > 0 || - BIO_ctrl_wpending(transport_bio_) > 0) { - return false; - } - - return transport_->socket()->IsConnectedAndIdle(); -} - -int SSLClientSocketOpenSSL::GetPeerAddress(IPEndPoint* addressList) const { - return transport_->socket()->GetPeerAddress(addressList); -} - -int SSLClientSocketOpenSSL::GetLocalAddress(IPEndPoint* addressList) const { - return transport_->socket()->GetLocalAddress(addressList); -} - -const BoundNetLog& SSLClientSocketOpenSSL::NetLog() const { - return net_log_; -} - -void SSLClientSocketOpenSSL::SetSubresourceSpeculation() { - if (transport_.get() && transport_->socket()) { - transport_->socket()->SetSubresourceSpeculation(); - } else { - NOTREACHED(); - } -} - -void SSLClientSocketOpenSSL::SetOmniboxSpeculation() { - if (transport_.get() && transport_->socket()) { - transport_->socket()->SetOmniboxSpeculation(); - } else { - NOTREACHED(); - } -} - -bool SSLClientSocketOpenSSL::WasEverUsed() const { - if (transport_.get() && transport_->socket()) - return transport_->socket()->WasEverUsed(); - - NOTREACHED(); - return false; -} - -bool SSLClientSocketOpenSSL::UsingTCPFastOpen() const { - if (transport_.get() && transport_->socket()) - return transport_->socket()->UsingTCPFastOpen(); - - NOTREACHED(); - return false; -} - -// Socket methods - -int SSLClientSocketOpenSSL::Read(IOBuffer* buf, - int buf_len, - const CompletionCallback& callback) { - user_read_buf_ = buf; - user_read_buf_len_ = buf_len; - - int rv = DoReadLoop(OK); - - if (rv == ERR_IO_PENDING) { - user_read_callback_ = callback; - } else { - user_read_buf_ = NULL; - user_read_buf_len_ = 0; - } +int SSLClientSocketOpenSSL::DoHandshakeLoop(int last_io_result) { + int rv = last_io_result; + do { + // Default to STATE_NONE for next state. + // (This is a quirk carried over from the windows + // implementation. It makes reading the logs a bit harder.) + // State handlers can and often do call GotoState just + // to stay in the current state. + State state = next_handshake_state_; + GotoState(STATE_NONE); + switch (state) { + case STATE_HANDSHAKE: + rv = DoHandshake(); + break; + case STATE_VERIFY_CERT: + DCHECK(rv == OK); + rv = DoVerifyCert(rv); + break; + case STATE_VERIFY_CERT_COMPLETE: + rv = DoVerifyCertComplete(rv); + break; + case STATE_NONE: + default: + rv = ERR_UNEXPECTED; + NOTREACHED() << "unexpected state" << state; + break; + } + bool network_moved = DoTransportIO(); + if (network_moved && next_handshake_state_ == STATE_HANDSHAKE) { + // In general we exit the loop if rv is ERR_IO_PENDING. In this + // special case we keep looping even if rv is ERR_IO_PENDING because + // the transport IO may allow DoHandshake to make progress. + rv = OK; // This causes us to stay in the loop. + } + } while (rv != ERR_IO_PENDING && next_handshake_state_ != STATE_NONE); return rv; } @@ -1381,24 +1146,6 @@ int SSLClientSocketOpenSSL::DoReadLoop(int result) { return rv; } -int SSLClientSocketOpenSSL::Write(IOBuffer* buf, - int buf_len, - const CompletionCallback& callback) { - user_write_buf_ = buf; - user_write_buf_len_ = buf_len; - - int rv = DoWriteLoop(OK); - - if (rv == ERR_IO_PENDING) { - user_write_callback_ = callback; - } else { - user_write_buf_ = NULL; - user_write_buf_len_ = 0; - } - - return rv; -} - int SSLClientSocketOpenSSL::DoWriteLoop(int result) { if (result < 0) return result; @@ -1413,14 +1160,6 @@ int SSLClientSocketOpenSSL::DoWriteLoop(int result) { return rv; } -bool SSLClientSocketOpenSSL::SetReceiveBufferSize(int32 size) { - return transport_->socket()->SetReceiveBufferSize(size); -} - -bool SSLClientSocketOpenSSL::SetSendBufferSize(int32 size) { - return transport_->socket()->SetSendBufferSize(size); -} - int SSLClientSocketOpenSSL::DoPayloadRead() { crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); @@ -1502,4 +1241,260 @@ int SSLClientSocketOpenSSL::DoPayloadWrite() { return MapOpenSSLError(err, err_tracer); } +int SSLClientSocketOpenSSL::BufferSend(void) { + if (transport_send_busy_) + return ERR_IO_PENDING; + + if (!send_buffer_.get()) { + // Get a fresh send buffer out of the send BIO. + size_t max_read = BIO_ctrl_pending(transport_bio_); + if (!max_read) + return 0; // Nothing pending in the OpenSSL write BIO. + send_buffer_ = new DrainableIOBuffer(new IOBuffer(max_read), max_read); + int read_bytes = BIO_read(transport_bio_, send_buffer_->data(), max_read); + DCHECK_GT(read_bytes, 0); + CHECK_EQ(static_cast<int>(max_read), read_bytes); + } + + int rv = transport_->socket()->Write( + send_buffer_.get(), + send_buffer_->BytesRemaining(), + base::Bind(&SSLClientSocketOpenSSL::BufferSendComplete, + base::Unretained(this))); + if (rv == ERR_IO_PENDING) { + transport_send_busy_ = true; + } else { + TransportWriteComplete(rv); + } + return rv; +} + +int SSLClientSocketOpenSSL::BufferRecv(void) { + if (transport_recv_busy_) + return ERR_IO_PENDING; + + // Determine how much was requested from |transport_bio_| that was not + // actually available. + size_t requested = BIO_ctrl_get_read_request(transport_bio_); + if (requested == 0) { + // This is not a perfect match of error codes, as no operation is + // actually pending. However, returning 0 would be interpreted as + // a possible sign of EOF, which is also an inappropriate match. + return ERR_IO_PENDING; + } + + // Known Issue: While only reading |requested| data is the more correct + // implementation, it has the downside of resulting in frequent reads: + // One read for the SSL record header (~5 bytes) and one read for the SSL + // record body. Rather than issuing these reads to the underlying socket + // (and constantly allocating new IOBuffers), a single Read() request to + // fill |transport_bio_| is issued. As long as an SSL client socket cannot + // be gracefully shutdown (via SSL close alerts) and re-used for non-SSL + // traffic, this over-subscribed Read()ing will not cause issues. + size_t max_write = BIO_ctrl_get_write_guarantee(transport_bio_); + if (!max_write) + return ERR_IO_PENDING; + + recv_buffer_ = new IOBuffer(max_write); + int rv = transport_->socket()->Read( + recv_buffer_.get(), + max_write, + base::Bind(&SSLClientSocketOpenSSL::BufferRecvComplete, + base::Unretained(this))); + if (rv == ERR_IO_PENDING) { + transport_recv_busy_ = true; + } else { + TransportReadComplete(rv); + } + return rv; +} + +void SSLClientSocketOpenSSL::BufferSendComplete(int result) { + transport_send_busy_ = false; + TransportWriteComplete(result); + OnSendComplete(result); +} + +void SSLClientSocketOpenSSL::BufferRecvComplete(int result) { + TransportReadComplete(result); + OnRecvComplete(result); +} + +void SSLClientSocketOpenSSL::TransportWriteComplete(int result) { + DCHECK(ERR_IO_PENDING != result); + if (result < 0) { + // Got a socket write error; close the BIO to indicate this upward. + DVLOG(1) << "TransportWriteComplete error " << result; + (void)BIO_shutdown_wr(transport_bio_); + BIO_set_mem_eof_return(transport_bio_, 0); + send_buffer_ = NULL; + } else { + DCHECK(send_buffer_.get()); + send_buffer_->DidConsume(result); + DCHECK_GE(send_buffer_->BytesRemaining(), 0); + if (send_buffer_->BytesRemaining() <= 0) + send_buffer_ = NULL; + } +} + +void SSLClientSocketOpenSSL::TransportReadComplete(int result) { + DCHECK(ERR_IO_PENDING != result); + if (result <= 0) { + DVLOG(1) << "TransportReadComplete result " << result; + // Received 0 (end of file) or an error. Either way, bubble it up to the + // SSL layer via the BIO. TODO(joth): consider stashing the error code, to + // relay up to the SSL socket client (i.e. via DoReadCallback). + if (result == 0) + transport_recv_eof_ = true; + BIO_set_mem_eof_return(transport_bio_, 0); + (void)BIO_shutdown_wr(transport_bio_); + } else { + DCHECK(recv_buffer_.get()); + int ret = BIO_write(transport_bio_, recv_buffer_->data(), result); + // A write into a memory BIO should always succeed. + CHECK_EQ(result, ret); + } + recv_buffer_ = NULL; + transport_recv_busy_ = false; +} + +int SSLClientSocketOpenSSL::ClientCertRequestCallback(SSL* ssl, + X509** x509, + EVP_PKEY** pkey) { + DVLOG(3) << "OpenSSL ClientCertRequestCallback called"; + DCHECK(ssl == ssl_); + DCHECK(*x509 == NULL); + DCHECK(*pkey == NULL); + + if (!ssl_config_.send_client_cert) { + // First pass: we know that a client certificate is needed, but we do not + // have one at hand. + client_auth_cert_needed_ = true; + STACK_OF(X509_NAME) *authorities = SSL_get_client_CA_list(ssl); + for (int i = 0; i < sk_X509_NAME_num(authorities); i++) { + X509_NAME *ca_name = (X509_NAME *)sk_X509_NAME_value(authorities, i); + unsigned char* str = NULL; + int length = i2d_X509_NAME(ca_name, &str); + cert_authorities_.push_back(std::string( + reinterpret_cast<const char*>(str), + static_cast<size_t>(length))); + OPENSSL_free(str); + } + + return -1; // Suspends handshake. + } + + // Second pass: a client certificate should have been selected. + if (ssl_config_.client_cert.get()) { + // A note about ownership: FetchClientCertPrivateKey() increments + // the reference count of the EVP_PKEY. Ownership of this reference + // is passed directly to OpenSSL, which will release the reference + // using EVP_PKEY_free() when the SSL object is destroyed. + OpenSSLClientKeyStore::ScopedEVP_PKEY privkey; + if (OpenSSLClientKeyStore::GetInstance()->FetchClientCertPrivateKey( + ssl_config_.client_cert.get(), &privkey)) { + // TODO(joth): (copied from NSS) We should wait for server certificate + // verification before sending our credentials. See http://crbug.com/13934 + *x509 = X509Certificate::DupOSCertHandle( + ssl_config_.client_cert->os_cert_handle()); + *pkey = privkey.release(); + return 1; + } + LOG(WARNING) << "Client cert found without private key"; + } + + // Send no client certificate. + return 0; +} + +void SSLClientSocketOpenSSL::ChannelIDRequestCallback(SSL* ssl, + EVP_PKEY** pkey) { + DVLOG(3) << "OpenSSL ChannelIDRequestCallback called"; + DCHECK_EQ(ssl, ssl_); + DCHECK(!*pkey); + + channel_id_xtn_negotiated_ = true; + if (!channel_id_private_key_.size()) { + channel_id_request_return_value_ = + server_bound_cert_service_->GetOrCreateDomainBoundCert( + host_and_port_.host(), + &channel_id_private_key_, + &channel_id_cert_, + base::Bind(&SSLClientSocketOpenSSL::OnHandshakeIOComplete, + base::Unretained(this)), + &channel_id_request_handle_); + if (channel_id_request_return_value_ != OK) + return; + } + + // Decode key. + std::vector<uint8> encrypted_private_key_info; + std::vector<uint8> subject_public_key_info; + encrypted_private_key_info.assign( + channel_id_private_key_.data(), + channel_id_private_key_.data() + channel_id_private_key_.size()); + subject_public_key_info.assign( + channel_id_cert_.data(), + channel_id_cert_.data() + channel_id_cert_.size()); + scoped_ptr<crypto::ECPrivateKey> ec_private_key( + crypto::ECPrivateKey::CreateFromEncryptedPrivateKeyInfo( + ServerBoundCertService::kEPKIPassword, + encrypted_private_key_info, + subject_public_key_info)); + set_channel_id_sent(true); + *pkey = EVP_PKEY_dup(ec_private_key->key()); +} + +// SelectNextProtoCallback is called by OpenSSL during the handshake. If the +// server supports NPN, selects a protocol from the list that the server +// provides. According to third_party/openssl/openssl/ssl/ssl_lib.c, the +// callback can assume that |in| is syntactically valid. +int SSLClientSocketOpenSSL::SelectNextProtoCallback(unsigned char** out, + unsigned char* outlen, + const unsigned char* in, + unsigned int inlen) { +#if defined(OPENSSL_NPN_NEGOTIATED) + if (ssl_config_.next_protos.empty()) { + *out = reinterpret_cast<uint8*>( + const_cast<char*>(kDefaultSupportedNPNProtocol)); + *outlen = arraysize(kDefaultSupportedNPNProtocol) - 1; + npn_status_ = kNextProtoUnsupported; + return SSL_TLSEXT_ERR_OK; + } + + // Assume there's no overlap between our protocols and the server's list. + npn_status_ = kNextProtoNoOverlap; + + // For each protocol in server preference order, see if we support it. + for (unsigned int i = 0; i < inlen; i += in[i] + 1) { + for (std::vector<std::string>::const_iterator + j = ssl_config_.next_protos.begin(); + j != ssl_config_.next_protos.end(); ++j) { + if (in[i] == j->size() && + memcmp(&in[i + 1], j->data(), in[i]) == 0) { + // We found a match. + *out = const_cast<unsigned char*>(in) + i + 1; + *outlen = in[i]; + npn_status_ = kNextProtoNegotiated; + break; + } + } + if (npn_status_ == kNextProtoNegotiated) + break; + } + + // If we didn't find a protocol, we select the first one from our list. + if (npn_status_ == kNextProtoNoOverlap) { + *out = reinterpret_cast<uint8*>(const_cast<char*>( + ssl_config_.next_protos[0].data())); + *outlen = ssl_config_.next_protos[0].size(); + } + + npn_proto_.assign(reinterpret_cast<const char*>(*out), *outlen); + server_protos_.assign(reinterpret_cast<const char*>(in), inlen); + DVLOG(2) << "next protocol: '" << npn_proto_ << "' status: " << npn_status_; +#endif + return SSL_TLSEXT_ERR_OK; +} + } // namespace net |