From b9b651f0e2f8d5ef82743af2c42180112613c05c Mon Sep 17 00:00:00 2001 From: "rsleevi@chromium.org" Date: Sat, 9 Nov 2013 04:32:22 +0000 Subject: 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 --- net/socket/ssl_client_socket_openssl.cc | 1443 +++++++++++++++---------------- 1 file changed, 719 insertions(+), 724 deletions(-) (limited to 'net/socket') 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,216 +465,185 @@ SSLClientSocketOpenSSL::~SSLClientSocketOpenSSL() { Disconnect(); } -bool SSLClientSocketOpenSSL::Init() { - DCHECK(!ssl_); - DCHECK(!transport_bio_); +void SSLClientSocketOpenSSL::GetSSLCertRequestInfo( + SSLCertRequestInfo* cert_request_info) { + cert_request_info->host_and_port = host_and_port_.ToString(); + cert_request_info->cert_authorities = cert_authorities_; +} - SSLContext* context = SSLContext::GetInstance(); - crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); +SSLClientSocket::NextProtoStatus SSLClientSocketOpenSSL::GetNextProto( + std::string* proto, std::string* server_protos) { + *proto = npn_proto_; + *server_protos = server_protos_; + return npn_status_; +} - ssl_ = SSL_new(context->ssl_ctx()); - if (!ssl_ || !context->SetClientSocketForSSL(ssl_, this)) - return false; +ServerBoundCertService* +SSLClientSocketOpenSSL::GetServerBoundCertService() const { + return server_bound_cert_service_; +} - if (!SSL_set_tlsext_host_name(ssl_, host_and_port_.host().c_str())) - return false; +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); - trying_cached_session_ = - context->session_cache()->SetSSLSession(ssl_, host_and_port_, - ssl_session_cache_shard_); + int rv = SSL_export_keying_material( + ssl_, out, outlen, const_cast(label.data()), + label.size(), + reinterpret_cast(const_cast(context.data())), + context.length(), + context.length() > 0); - BIO* ssl_bio = NULL; - // 0 => use default buffer sizes. - if (!BIO_new_bio_pair(&ssl_bio, 0, &transport_bio_, 0)) - return false; - DCHECK(ssl_bio); - DCHECK(transport_bio_); + 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; +} - SSL_set_bio(ssl_, ssl_bio, ssl_bio); +int SSLClientSocketOpenSSL::GetTLSUniqueChannelBinding(std::string* out) { + return ERR_NOT_IMPLEMENTED; +} - // OpenSSL defaults some options to on, others to off. To avoid ambiguity, - // set everything we care about to an absolute value. - SslSetClearMask options; - options.ConfigureFlag(SSL_OP_NO_SSLv2, true); - bool ssl3_enabled = (ssl_config_.version_min == SSL_PROTOCOL_VERSION_SSL3); - options.ConfigureFlag(SSL_OP_NO_SSLv3, !ssl3_enabled); - bool tls1_enabled = (ssl_config_.version_min <= SSL_PROTOCOL_VERSION_TLS1 && - ssl_config_.version_max >= SSL_PROTOCOL_VERSION_TLS1); - options.ConfigureFlag(SSL_OP_NO_TLSv1, !tls1_enabled); -#if defined(SSL_OP_NO_TLSv1_1) - bool tls1_1_enabled = - (ssl_config_.version_min <= SSL_PROTOCOL_VERSION_TLS1_1 && - ssl_config_.version_max >= SSL_PROTOCOL_VERSION_TLS1_1); - options.ConfigureFlag(SSL_OP_NO_TLSv1_1, !tls1_1_enabled); -#endif -#if defined(SSL_OP_NO_TLSv1_2) - bool tls1_2_enabled = - (ssl_config_.version_min <= SSL_PROTOCOL_VERSION_TLS1_2 && - ssl_config_.version_max >= SSL_PROTOCOL_VERSION_TLS1_2); - options.ConfigureFlag(SSL_OP_NO_TLSv1_2, !tls1_2_enabled); -#endif +int SSLClientSocketOpenSSL::Connect(const CompletionCallback& callback) { + net_log_.BeginEvent(NetLog::TYPE_SSL_CONNECT); -#if defined(SSL_OP_NO_COMPRESSION) - options.ConfigureFlag(SSL_OP_NO_COMPRESSION, true); -#endif + // Set up new ssl object. + if (!Init()) { + int result = ERR_UNEXPECTED; + net_log_.EndEventWithNetErrorCode(NetLog::TYPE_SSL_CONNECT, result); + return result; + } - // TODO(joth): Set this conditionally, see http://crbug.com/55410 - options.ConfigureFlag(SSL_OP_LEGACY_SERVER_CONNECT, true); + // Set SSL to client mode. Handshake happens in the loop below. + SSL_set_connect_state(ssl_); - SSL_set_options(ssl_, options.set_mask); - SSL_clear_options(ssl_, options.clear_mask); + 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); + } - // Same as above, this time for the SSL mode. - SslSetClearMask mode; + return rv > OK ? OK : rv; +} -#if defined(SSL_MODE_RELEASE_BUFFERS) - mode.ConfigureFlag(SSL_MODE_RELEASE_BUFFERS, true); -#endif +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; + } -#if defined(SSL_MODE_SMALL_BUFFERS) - mode.ConfigureFlag(SSL_MODE_SMALL_BUFFERS, true); -#endif + // Shut down anything that may call us back. + verifier_.reset(); + transport_->socket()->Disconnect(); - SSL_set_mode(ssl_, mode.set_mask); - SSL_clear_mode(ssl_, mode.clear_mask); + // Null all callbacks, delete all buffers. + transport_send_busy_ = false; + send_buffer_ = NULL; + transport_recv_busy_ = false; + transport_recv_eof_ = false; + recv_buffer_ = NULL; - // Removing ciphers by ID from OpenSSL is a bit involved as we must use the - // textual name with SSL_set_cipher_list because there is no public API to - // directly remove a cipher by ID. - STACK_OF(SSL_CIPHER)* ciphers = SSL_get_ciphers(ssl_); - DCHECK(ciphers); - // See SSLConfig::disabled_cipher_suites for description of the suites - // disabled by default. Note that !SHA256 and !SHA384 only remove HMAC-SHA256 - // and HMAC-SHA384 cipher suites, not GCM cipher suites with SHA256 or SHA384 - // as the handshake hash. - std::string command("DEFAULT:!NULL:!aNULL:!IDEA:!FZA:!SRP:!SHA256:!SHA384:" - "!aECDH:!AESGCM+AES256"); - // Walk through all the installed ciphers, seeing if any need to be - // appended to the cipher removal |command|. - for (int i = 0; i < sk_SSL_CIPHER_num(ciphers); ++i) { - const SSL_CIPHER* cipher = sk_SSL_CIPHER_value(ciphers, i); - const uint16 id = SSL_CIPHER_get_id(cipher); - // Remove any ciphers with a strength of less than 80 bits. Note the NSS - // implementation uses "effective" bits here but OpenSSL does not provide - // this detail. This only impacts Triple DES: reports 112 vs. 168 bits, - // both of which are greater than 80 anyway. - bool disable = SSL_CIPHER_get_bits(cipher, NULL) < 80; - if (!disable) { - disable = std::find(ssl_config_.disabled_cipher_suites.begin(), - ssl_config_.disabled_cipher_suites.end(), id) != - ssl_config_.disabled_cipher_suites.end(); - } - if (disable) { - const char* name = SSL_CIPHER_get_name(cipher); - DVLOG(3) << "Found cipher to remove: '" << name << "', ID: " << id - << " strength: " << SSL_CIPHER_get_bits(cipher, NULL); - command.append(":!"); - command.append(name); - } - } - int rv = SSL_set_cipher_list(ssl_, command.c_str()); - // If this fails (rv = 0) it means there are no ciphers enabled on this SSL. - // This will almost certainly result in the socket failing to complete the - // handshake at which point the appropriate error is bubbled up to the client. - LOG_IF(WARNING, rv != 1) << "SSL_set_cipher_list('" << command << "') " - "returned " << rv; + 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; - // TLS channel ids. - if (IsChannelIDEnabled(ssl_config_, server_bound_cert_service_)) { - SSL_enable_tls_channel_id(ssl_); - } + server_cert_verify_result_.Reset(); + completed_handshake_ = false; - return true; + cert_authorities_.clear(); + client_auth_cert_needed_ = 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); +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; - 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(str), - static_cast(length))); - OPENSSL_free(str); - } + return transport_->socket()->IsConnected(); +} - return -1; // Suspends handshake. +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; } - // 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"; - } + return transport_->socket()->IsConnectedAndIdle(); +} - // Send no client certificate. - return 0; +int SSLClientSocketOpenSSL::GetPeerAddress(IPEndPoint* addressList) const { + return transport_->socket()->GetPeerAddress(addressList); } -void SSLClientSocketOpenSSL::ChannelIDRequestCallback(SSL* ssl, - EVP_PKEY** pkey) { - DVLOG(3) << "OpenSSL ChannelIDRequestCallback called"; - DCHECK_EQ(ssl, ssl_); - DCHECK(!*pkey); +int SSLClientSocketOpenSSL::GetLocalAddress(IPEndPoint* addressList) const { + return transport_->socket()->GetLocalAddress(addressList); +} - 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; +const BoundNetLog& SSLClientSocketOpenSSL::NetLog() const { + return net_log_; +} + +void SSLClientSocketOpenSSL::SetSubresourceSpeculation() { + if (transport_.get() && transport_->socket()) { + transport_->socket()->SetSubresourceSpeculation(); + } else { + NOTREACHED(); } +} - // Decode key. - std::vector encrypted_private_key_info; - std::vector 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 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()); +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; } -// SSLClientSocket methods +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(); @@ -696,78 +665,199 @@ bool SSLClientSocketOpenSSL::GetSSLInfo(SSLInfo* ssl_info) { 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_); + 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(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_); + + SSLContext* context = SSLContext::GetInstance(); + crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); + + ssl_ = SSL_new(context->ssl_ctx()); + if (!ssl_ || !context->SetClientSocketForSSL(ssl_, this)) + return false; + + if (!SSL_set_tlsext_host_name(ssl_, host_and_port_.host().c_str())) + return false; + + trying_cached_session_ = + context->session_cache()->SetSSLSession(ssl_, host_and_port_, + ssl_session_cache_shard_); + + BIO* ssl_bio = NULL; + // 0 => use default buffer sizes. + if (!BIO_new_bio_pair(&ssl_bio, 0, &transport_bio_, 0)) + return false; + DCHECK(ssl_bio); + DCHECK(transport_bio_); - ssl_info->connection_status = EncodeSSLConnectionStatus( - SSL_CIPHER_get_id(cipher), - compression ? compression->type : 0, - GetNetSSLVersion(ssl_)); + SSL_set_bio(ssl_, ssl_bio, ssl_bio); - 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(peer_supports_renego_ext), 2); + // OpenSSL defaults some options to on, others to off. To avoid ambiguity, + // set everything we care about to an absolute value. + SslSetClearMask options; + options.ConfigureFlag(SSL_OP_NO_SSLv2, true); + bool ssl3_enabled = (ssl_config_.version_min == SSL_PROTOCOL_VERSION_SSL3); + options.ConfigureFlag(SSL_OP_NO_SSLv3, !ssl3_enabled); + bool tls1_enabled = (ssl_config_.version_min <= SSL_PROTOCOL_VERSION_TLS1 && + ssl_config_.version_max >= SSL_PROTOCOL_VERSION_TLS1); + options.ConfigureFlag(SSL_OP_NO_TLSv1, !tls1_enabled); +#if defined(SSL_OP_NO_TLSv1_1) + bool tls1_1_enabled = + (ssl_config_.version_min <= SSL_PROTOCOL_VERSION_TLS1_1 && + ssl_config_.version_max >= SSL_PROTOCOL_VERSION_TLS1_1); + options.ConfigureFlag(SSL_OP_NO_TLSv1_1, !tls1_1_enabled); +#endif +#if defined(SSL_OP_NO_TLSv1_2) + bool tls1_2_enabled = + (ssl_config_.version_min <= SSL_PROTOCOL_VERSION_TLS1_2 && + ssl_config_.version_max >= SSL_PROTOCOL_VERSION_TLS1_2); + options.ConfigureFlag(SSL_OP_NO_TLSv1_2, !tls1_2_enabled); +#endif - if (ssl_config_.version_fallback) - ssl_info->connection_status |= SSL_CONNECTION_VERSION_FALLBACK; +#if defined(SSL_OP_NO_COMPRESSION) + options.ConfigureFlag(SSL_OP_NO_COMPRESSION, true); +#endif - ssl_info->handshake_type = SSL_session_reused(ssl_) ? - SSLInfo::HANDSHAKE_RESUME : SSLInfo::HANDSHAKE_FULL; + // TODO(joth): Set this conditionally, see http://crbug.com/55410 + options.ConfigureFlag(SSL_OP_LEGACY_SERVER_CONNECT, true); - DVLOG(3) << "Encoded connection status: cipher suite = " - << SSLConnectionStatusToCipherSuite(ssl_info->connection_status) - << " version = " - << SSLConnectionStatusToVersion(ssl_info->connection_status); - return true; -} + SSL_set_options(ssl_, options.set_mask); + SSL_clear_options(ssl_, options.clear_mask); -void SSLClientSocketOpenSSL::GetSSLCertRequestInfo( - SSLCertRequestInfo* cert_request_info) { - cert_request_info->host_and_port = host_and_port_.ToString(); - cert_request_info->cert_authorities = cert_authorities_; -} + // Same as above, this time for the SSL mode. + SslSetClearMask mode; -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); +#if defined(SSL_MODE_RELEASE_BUFFERS) + mode.ConfigureFlag(SSL_MODE_RELEASE_BUFFERS, true); +#endif - int rv = SSL_export_keying_material( - ssl_, out, outlen, const_cast(label.data()), - label.size(), - reinterpret_cast(const_cast(context.data())), - context.length(), - context.length() > 0); +#if defined(SSL_MODE_SMALL_BUFFERS) + mode.ConfigureFlag(SSL_MODE_SMALL_BUFFERS, true); +#endif - 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; -} + SSL_set_mode(ssl_, mode.set_mask); + SSL_clear_mode(ssl_, mode.clear_mask); -int SSLClientSocketOpenSSL::GetTLSUniqueChannelBinding(std::string* out) { - return ERR_NOT_IMPLEMENTED; -} + // Removing ciphers by ID from OpenSSL is a bit involved as we must use the + // textual name with SSL_set_cipher_list because there is no public API to + // directly remove a cipher by ID. + STACK_OF(SSL_CIPHER)* ciphers = SSL_get_ciphers(ssl_); + DCHECK(ciphers); + // See SSLConfig::disabled_cipher_suites for description of the suites + // disabled by default. Note that !SHA256 and !SHA384 only remove HMAC-SHA256 + // and HMAC-SHA384 cipher suites, not GCM cipher suites with SHA256 or SHA384 + // as the handshake hash. + std::string command("DEFAULT:!NULL:!aNULL:!IDEA:!FZA:!SRP:!SHA256:!SHA384:" + "!aECDH:!AESGCM+AES256"); + // Walk through all the installed ciphers, seeing if any need to be + // appended to the cipher removal |command|. + for (int i = 0; i < sk_SSL_CIPHER_num(ciphers); ++i) { + const SSL_CIPHER* cipher = sk_SSL_CIPHER_value(ciphers, i); + const uint16 id = SSL_CIPHER_get_id(cipher); + // Remove any ciphers with a strength of less than 80 bits. Note the NSS + // implementation uses "effective" bits here but OpenSSL does not provide + // this detail. This only impacts Triple DES: reports 112 vs. 168 bits, + // both of which are greater than 80 anyway. + bool disable = SSL_CIPHER_get_bits(cipher, NULL) < 80; + if (!disable) { + disable = std::find(ssl_config_.disabled_cipher_suites.begin(), + ssl_config_.disabled_cipher_suites.end(), id) != + ssl_config_.disabled_cipher_suites.end(); + } + if (disable) { + const char* name = SSL_CIPHER_get_name(cipher); + DVLOG(3) << "Found cipher to remove: '" << name << "', ID: " << id + << " strength: " << SSL_CIPHER_get_bits(cipher, NULL); + command.append(":!"); + command.append(name); + } + } + int rv = SSL_set_cipher_list(ssl_, command.c_str()); + // If this fails (rv = 0) it means there are no ciphers enabled on this SSL. + // This will almost certainly result in the socket failing to complete the + // handshake at which point the appropriate error is bubbled up to the client. + LOG_IF(WARNING, rv != 1) << "SSL_set_cipher_list('" << command << "') " + "returned " << rv; -SSLClientSocket::NextProtoStatus SSLClientSocketOpenSSL::GetNextProto( - std::string* proto, std::string* server_protos) { - *proto = npn_proto_; - *server_protos = server_protos_; - return npn_status_; -} + // TLS channel ids. + if (IsChannelIDEnabled(ssl_config_, server_bound_cert_service_)) { + SSL_enable_tls_channel_id(ssl_); + } -ServerBoundCertService* -SSLClientSocketOpenSSL::GetServerBoundCertService() const { - return server_bound_cert_service_; + return true; } void SSLClientSocketOpenSSL::DoReadCallback(int rv) { @@ -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() { @@ -927,284 +929,76 @@ int SSLClientSocketOpenSSL::DoHandshake() { if (ssl_error == SSL_ERROR_WANT_CHANNEL_ID_LOOKUP) { // The server supports TLS channel id and the lookup is asynchronous. - // Retrieve the error from the call to |server_bound_cert_service_|. - net_error = channel_id_request_return_value_; - } else { - net_error = MapOpenSSLError(ssl_error, err_tracer); - } - - // If not done, stay in this state - if (net_error == ERR_IO_PENDING) { - GotoState(STATE_HANDSHAKE); - } else { - LOG(ERROR) << "handshake failed; returned " << rv - << ", SSL error code " << ssl_error - << ", net_error " << net_error; - net_log_.AddEvent( - NetLog::TYPE_SSL_HANDSHAKE_ERROR, - CreateNetLogSSLErrorCallback(net_error, ssl_error)); - } - } - 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( - const_cast(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::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(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(const_cast( - ssl_config_.next_protos[0].data())); - *outlen = ssl_config_.next_protos[0].size(); - } - - npn_proto_.assign(reinterpret_cast(*out), *outlen); - server_protos_.assign(reinterpret_cast(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); - - CertStatus cert_status; - if (ssl_config_.IsAllowedBadCert(server_cert_.get(), &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; - server_cert_verify_result_.verified_cert = server_cert_; - return OK; - } - - int flags = 0; - if (ssl_config_.rev_checking_enabled) - flags |= CertVerifier::VERIFY_REV_CHECKING_ENABLED; - if (ssl_config_.verify_ev_cert) - flags |= CertVerifier::VERIFY_EV_CERT; - if (ssl_config_.cert_io_enabled) - flags |= CertVerifier::VERIFY_CERT_IO_ENABLED; - if (ssl_config_.rev_checking_required_local_anchors) - flags |= CertVerifier::VERIFY_REV_CHECKING_REQUIRED_LOCAL_ANCHORS; - verifier_.reset(new SingleRequestCertVerifier(cert_verifier_)); - return verifier_->Verify( - server_cert_.get(), - host_and_port_.host(), - flags, - NULL /* no CRL set */, - &server_cert_verify_result_, - base::Bind(&SSLClientSocketOpenSSL::OnHandshakeIOComplete, - base::Unretained(this)), - net_log_); -} - -int SSLClientSocketOpenSSL::DoVerifyCertComplete(int result) { - verifier_.reset(); - - if (result == OK) { - // TODO(joth): Work out if we need to remember the intermediate CA certs - // when the server sends them to us, and do so here. - } else { - DVLOG(1) << "DoVerifyCertComplete error " << ErrorToString(result) - << " (" << result << ")"; - } - - completed_handshake_ = true; - // Exit DoHandshakeLoop and return the result to the caller to Connect. - DCHECK_EQ(STATE_NONE, next_handshake_state_); - return result; -} - -X509Certificate* SSLClientSocketOpenSSL::UpdateServerCert() { - if (server_cert_.get()) - return server_cert_.get(); - - crypto::ScopedOpenSSL cert(SSL_get_peer_certificate(ssl_)); - if (!cert.get()) { - LOG(WARNING) << "SSL_get_peer_certificate returned NULL"; - return NULL; - } - - // Unlike SSL_get_peer_certificate, SSL_get_peer_cert_chain does not - // increment the reference so sk_X509_free does not need to be called. - STACK_OF(X509)* chain = SSL_get_peer_cert_chain(ssl_); - X509Certificate::OSCertHandles intermediates; - if (chain) { - for (int i = 0; i < sk_X509_num(chain); ++i) - intermediates.push_back(sk_X509_value(chain, i)); - } - server_cert_ = X509Certificate::CreateFromHandle(cert.get(), intermediates); - DCHECK(server_cert_.get()); - - 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(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); + // Retrieve the error from the call to |server_bound_cert_service_|. + net_error = channel_id_request_return_value_; + } else { + net_error = MapOpenSSLError(ssl_error, err_tracer); + } + + // If not done, stay in this state + if (net_error == ERR_IO_PENDING) { + GotoState(STATE_HANDSHAKE); + } else { + LOG(ERROR) << "handshake failed; returned " << rv + << ", SSL error code " << ssl_error + << ", net_error " << net_error; + net_log_.AddEvent( + NetLog::TYPE_SSL_HANDSHAKE_ERROR, + CreateNetLogSSLErrorCallback(net_error, ssl_error)); + } } - return rv; + return net_error; } -void SSLClientSocketOpenSSL::BufferRecvComplete(int result) { - TransportReadComplete(result); - OnRecvComplete(result); +int SSLClientSocketOpenSSL::DoVerifyCert(int result) { + DCHECK(server_cert_.get()); + GotoState(STATE_VERIFY_CERT_COMPLETE); + + CertStatus cert_status; + if (ssl_config_.IsAllowedBadCert(server_cert_.get(), &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; + server_cert_verify_result_.verified_cert = server_cert_; + return OK; + } + + int flags = 0; + if (ssl_config_.rev_checking_enabled) + flags |= CertVerifier::VERIFY_REV_CHECKING_ENABLED; + if (ssl_config_.verify_ev_cert) + flags |= CertVerifier::VERIFY_EV_CERT; + if (ssl_config_.cert_io_enabled) + flags |= CertVerifier::VERIFY_CERT_IO_ENABLED; + if (ssl_config_.rev_checking_required_local_anchors) + flags |= CertVerifier::VERIFY_REV_CHECKING_REQUIRED_LOCAL_ANCHORS; + verifier_.reset(new SingleRequestCertVerifier(cert_verifier_)); + return verifier_->Verify( + server_cert_.get(), + host_and_port_.host(), + flags, + NULL /* no CRL set */, + &server_cert_verify_result_, + base::Bind(&SSLClientSocketOpenSSL::OnHandshakeIOComplete, + base::Unretained(this)), + net_log_); } -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_); +int SSLClientSocketOpenSSL::DoVerifyCertComplete(int result) { + verifier_.reset(); + + if (result == OK) { + // TODO(joth): Work out if we need to remember the intermediate CA certs + // when the server sends them to us, and do so here. } 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); + DVLOG(1) << "DoVerifyCertComplete error " << ErrorToString(result) + << " (" << result << ")"; } - recv_buffer_ = NULL; - transport_recv_busy_ = false; + + completed_handshake_ = true; + // Exit DoHandshakeLoop and return the result to the caller to Connect. + DCHECK_EQ(STATE_NONE, next_handshake_state_); + return result; } void SSLClientSocketOpenSSL::DoConnectCallback(int rv) { @@ -1215,6 +1009,30 @@ void SSLClientSocketOpenSSL::DoConnectCallback(int rv) { } } +X509Certificate* SSLClientSocketOpenSSL::UpdateServerCert() { + if (server_cert_.get()) + return server_cert_.get(); + + crypto::ScopedOpenSSL cert(SSL_get_peer_certificate(ssl_)); + if (!cert.get()) { + LOG(WARNING) << "SSL_get_peer_certificate returned NULL"; + return NULL; + } + + // Unlike SSL_get_peer_certificate, SSL_get_peer_cert_chain does not + // increment the reference so sk_X509_free does not need to be called. + STACK_OF(X509)* chain = SSL_get_peer_cert_chain(ssl_); + X509Certificate::OSCertHandles intermediates; + if (chain) { + for (int i = 0; i < sk_X509_num(chain); ++i) + intermediates.push_back(sk_X509_value(chain, i)); + } + server_cert_ = X509Certificate::CreateFromHandle(cert.get(), intermediates); + DCHECK(server_cert_.get()); + + return server_cert_.get(); +} + 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(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(str), + static_cast(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 encrypted_private_key_info; + std::vector 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 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( + const_cast(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::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(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(const_cast( + ssl_config_.next_protos[0].data())); + *outlen = ssl_config_.next_protos[0].size(); + } + + npn_proto_.assign(reinterpret_cast(*out), *outlen); + server_protos_.assign(reinterpret_cast(in), inlen); + DVLOG(2) << "next protocol: '" << npn_proto_ << "' status: " << npn_status_; +#endif + return SSL_TLSEXT_ERR_OK; +} + } // namespace net -- cgit v1.1