| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "net/socket/ssl_server_socket_impl.h" |
| |
| #include <openssl/err.h> |
| #include <openssl/ssl.h> |
| #include <utility> |
| |
| #include "base/callback_helpers.h" |
| #include "base/logging.h" |
| #include "base/strings/string_util.h" |
| #include "crypto/openssl_util.h" |
| #include "crypto/rsa_private_key.h" |
| #include "crypto/scoped_openssl_types.h" |
| #include "net/base/net_errors.h" |
| #include "net/cert/cert_verify_result.h" |
| #include "net/cert/client_cert_verifier.h" |
| #include "net/cert/x509_util_openssl.h" |
| #include "net/ssl/openssl_ssl_util.h" |
| #include "net/ssl/ssl_connection_status_flags.h" |
| #include "net/ssl/ssl_info.h" |
| |
| #define GotoState(s) next_handshake_state_ = s |
| |
| namespace net { |
| |
| namespace { |
| |
| // Creates an X509Certificate out of the concatenation of |cert|, if non-null, |
| // with |chain|. |
| scoped_refptr<X509Certificate> CreateX509Certificate(X509* cert, |
| STACK_OF(X509) * chain) { |
| std::vector<base::StringPiece> der_chain; |
| base::StringPiece der_cert; |
| scoped_refptr<X509Certificate> client_cert; |
| if (cert) { |
| if (!x509_util::GetDER(cert, &der_cert)) |
| return nullptr; |
| der_chain.push_back(der_cert); |
| } |
| |
| for (size_t i = 0; i < sk_X509_num(chain); ++i) { |
| X509* x = sk_X509_value(chain, i); |
| if (!x509_util::GetDER(x, &der_cert)) |
| return nullptr; |
| der_chain.push_back(der_cert); |
| } |
| |
| return X509Certificate::CreateFromDERCertChain(der_chain); |
| } |
| |
| class SSLServerSocketImpl : public SSLServerSocket { |
| public: |
| // See comments on CreateSSLServerSocket for details of how these |
| // parameters are used. |
| SSLServerSocketImpl(std::unique_ptr<StreamSocket> socket, SSL* ssl); |
| ~SSLServerSocketImpl() override; |
| |
| // SSLServerSocket interface. |
| int Handshake(const CompletionCallback& callback) override; |
| |
| // SSLSocket interface. |
| int ExportKeyingMaterial(const base::StringPiece& label, |
| bool has_context, |
| const base::StringPiece& context, |
| unsigned char* out, |
| unsigned int outlen) override; |
| |
| // Socket interface (via StreamSocket). |
| int Read(IOBuffer* buf, |
| int buf_len, |
| const CompletionCallback& callback) override; |
| int Write(IOBuffer* buf, |
| int buf_len, |
| const CompletionCallback& callback) override; |
| int SetReceiveBufferSize(int32_t size) override; |
| int SetSendBufferSize(int32_t size) override; |
| |
| // StreamSocket implementation. |
| int Connect(const CompletionCallback& callback) override; |
| void Disconnect() override; |
| bool IsConnected() const override; |
| bool IsConnectedAndIdle() const override; |
| int GetPeerAddress(IPEndPoint* address) const override; |
| int GetLocalAddress(IPEndPoint* address) const override; |
| const BoundNetLog& NetLog() const override; |
| void SetSubresourceSpeculation() override; |
| void SetOmniboxSpeculation() override; |
| bool WasEverUsed() const override; |
| bool WasNpnNegotiated() const override; |
| NextProto GetNegotiatedProtocol() const override; |
| bool GetSSLInfo(SSLInfo* ssl_info) override; |
| void GetConnectionAttempts(ConnectionAttempts* out) const override; |
| void ClearConnectionAttempts() override {} |
| void AddConnectionAttempts(const ConnectionAttempts& attempts) override {} |
| int64_t GetTotalReceivedBytes() const override; |
| static int CertVerifyCallback(X509_STORE_CTX* store_ctx, void* arg); |
| |
| private: |
| enum State { |
| STATE_NONE, |
| STATE_HANDSHAKE, |
| }; |
| |
| void OnSendComplete(int result); |
| void OnRecvComplete(int result); |
| void OnHandshakeIOComplete(int result); |
| |
| int BufferSend(); |
| void BufferSendComplete(int result); |
| void TransportWriteComplete(int result); |
| int BufferRecv(); |
| void BufferRecvComplete(int result); |
| int TransportReadComplete(int result); |
| bool DoTransportIO(); |
| int DoPayloadRead(); |
| int DoPayloadWrite(); |
| |
| int DoHandshakeLoop(int last_io_result); |
| int DoReadLoop(int result); |
| int DoWriteLoop(int result); |
| int DoHandshake(); |
| void DoHandshakeCallback(int result); |
| void DoReadCallback(int result); |
| void DoWriteCallback(int result); |
| |
| int Init(); |
| void ExtractClientCert(); |
| |
| // Members used to send and receive buffer. |
| bool transport_send_busy_; |
| bool transport_recv_busy_; |
| bool transport_recv_eof_; |
| |
| scoped_refptr<DrainableIOBuffer> send_buffer_; |
| scoped_refptr<IOBuffer> recv_buffer_; |
| |
| BoundNetLog net_log_; |
| |
| CompletionCallback user_handshake_callback_; |
| CompletionCallback user_read_callback_; |
| CompletionCallback user_write_callback_; |
| |
| // Used by Read function. |
| scoped_refptr<IOBuffer> user_read_buf_; |
| int user_read_buf_len_; |
| |
| // Used by Write function. |
| scoped_refptr<IOBuffer> user_write_buf_; |
| int user_write_buf_len_; |
| |
| // Used by TransportWriteComplete() and TransportReadComplete() to signify an |
| // error writing to the transport socket. A value of OK indicates no error. |
| int transport_write_error_; |
| |
| // OpenSSL stuff |
| SSL* ssl_; |
| BIO* transport_bio_; |
| |
| // StreamSocket for sending and receiving data. |
| std::unique_ptr<StreamSocket> transport_socket_; |
| |
| // Certificate for the client. |
| scoped_refptr<X509Certificate> client_cert_; |
| |
| State next_handshake_state_; |
| bool completed_handshake_; |
| |
| DISALLOW_COPY_AND_ASSIGN(SSLServerSocketImpl); |
| }; |
| |
| SSLServerSocketImpl::SSLServerSocketImpl( |
| std::unique_ptr<StreamSocket> transport_socket, |
| SSL* ssl) |
| : transport_send_busy_(false), |
| transport_recv_busy_(false), |
| transport_recv_eof_(false), |
| user_read_buf_len_(0), |
| user_write_buf_len_(0), |
| transport_write_error_(OK), |
| ssl_(ssl), |
| transport_bio_(NULL), |
| transport_socket_(std::move(transport_socket)), |
| next_handshake_state_(STATE_NONE), |
| completed_handshake_(false) {} |
| |
| SSLServerSocketImpl::~SSLServerSocketImpl() { |
| 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; |
| } |
| } |
| |
| int SSLServerSocketImpl::Handshake(const CompletionCallback& callback) { |
| net_log_.BeginEvent(NetLog::TYPE_SSL_SERVER_HANDSHAKE); |
| |
| // Set up new ssl object. |
| int rv = Init(); |
| if (rv != OK) { |
| LOG(ERROR) << "Failed to initialize OpenSSL: rv=" << rv; |
| net_log_.EndEventWithNetErrorCode(NetLog::TYPE_SSL_SERVER_HANDSHAKE, rv); |
| return rv; |
| } |
| |
| // Set SSL to server mode. Handshake happens in the loop below. |
| SSL_set_accept_state(ssl_); |
| |
| GotoState(STATE_HANDSHAKE); |
| rv = DoHandshakeLoop(OK); |
| if (rv == ERR_IO_PENDING) { |
| user_handshake_callback_ = callback; |
| } else { |
| net_log_.EndEventWithNetErrorCode(NetLog::TYPE_SSL_SERVER_HANDSHAKE, rv); |
| } |
| |
| return rv > OK ? OK : rv; |
| } |
| |
| int SSLServerSocketImpl::ExportKeyingMaterial(const base::StringPiece& label, |
| bool has_context, |
| const base::StringPiece& context, |
| unsigned char* out, |
| unsigned int outlen) { |
| if (!IsConnected()) |
| return ERR_SOCKET_NOT_CONNECTED; |
| |
| crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); |
| |
| int rv = SSL_export_keying_material( |
| ssl_, out, outlen, label.data(), label.size(), |
| reinterpret_cast<const unsigned 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 SSLServerSocketImpl::Read(IOBuffer* buf, |
| int buf_len, |
| const CompletionCallback& callback) { |
| DCHECK(user_read_callback_.is_null()); |
| DCHECK(user_handshake_callback_.is_null()); |
| DCHECK(!user_read_buf_); |
| DCHECK(!callback.is_null()); |
| |
| user_read_buf_ = buf; |
| user_read_buf_len_ = buf_len; |
| |
| DCHECK(completed_handshake_); |
| |
| 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 SSLServerSocketImpl::Write(IOBuffer* buf, |
| int buf_len, |
| const CompletionCallback& callback) { |
| DCHECK(user_write_callback_.is_null()); |
| DCHECK(!user_write_buf_); |
| DCHECK(!callback.is_null()); |
| |
| 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 SSLServerSocketImpl::SetReceiveBufferSize(int32_t size) { |
| return transport_socket_->SetReceiveBufferSize(size); |
| } |
| |
| int SSLServerSocketImpl::SetSendBufferSize(int32_t size) { |
| return transport_socket_->SetSendBufferSize(size); |
| } |
| |
| int SSLServerSocketImpl::Connect(const CompletionCallback& callback) { |
| NOTIMPLEMENTED(); |
| return ERR_NOT_IMPLEMENTED; |
| } |
| |
| void SSLServerSocketImpl::Disconnect() { |
| transport_socket_->Disconnect(); |
| } |
| |
| bool SSLServerSocketImpl::IsConnected() const { |
| // TODO(wtc): Find out if we should check transport_socket_->IsConnected() |
| // as well. |
| return completed_handshake_; |
| } |
| |
| bool SSLServerSocketImpl::IsConnectedAndIdle() const { |
| return completed_handshake_ && transport_socket_->IsConnectedAndIdle(); |
| } |
| |
| int SSLServerSocketImpl::GetPeerAddress(IPEndPoint* address) const { |
| if (!IsConnected()) |
| return ERR_SOCKET_NOT_CONNECTED; |
| return transport_socket_->GetPeerAddress(address); |
| } |
| |
| int SSLServerSocketImpl::GetLocalAddress(IPEndPoint* address) const { |
| if (!IsConnected()) |
| return ERR_SOCKET_NOT_CONNECTED; |
| return transport_socket_->GetLocalAddress(address); |
| } |
| |
| const BoundNetLog& SSLServerSocketImpl::NetLog() const { |
| return net_log_; |
| } |
| |
| void SSLServerSocketImpl::SetSubresourceSpeculation() { |
| transport_socket_->SetSubresourceSpeculation(); |
| } |
| |
| void SSLServerSocketImpl::SetOmniboxSpeculation() { |
| transport_socket_->SetOmniboxSpeculation(); |
| } |
| |
| bool SSLServerSocketImpl::WasEverUsed() const { |
| return transport_socket_->WasEverUsed(); |
| } |
| |
| bool SSLServerSocketImpl::WasNpnNegotiated() const { |
| NOTIMPLEMENTED(); |
| return false; |
| } |
| |
| NextProto SSLServerSocketImpl::GetNegotiatedProtocol() const { |
| // NPN is not supported by this class. |
| return kProtoUnknown; |
| } |
| |
| bool SSLServerSocketImpl::GetSSLInfo(SSLInfo* ssl_info) { |
| ssl_info->Reset(); |
| if (!completed_handshake_) |
| return false; |
| |
| ssl_info->cert = client_cert_; |
| |
| const SSL_CIPHER* cipher = SSL_get_current_cipher(ssl_); |
| CHECK(cipher); |
| ssl_info->security_bits = SSL_CIPHER_get_bits(cipher, NULL); |
| |
| SSLConnectionStatusSetCipherSuite( |
| static_cast<uint16_t>(SSL_CIPHER_get_id(cipher)), |
| &ssl_info->connection_status); |
| SSLConnectionStatusSetVersion(GetNetSSLVersion(ssl_), |
| &ssl_info->connection_status); |
| |
| if (!SSL_get_secure_renegotiation_support(ssl_)) |
| ssl_info->connection_status |= SSL_CONNECTION_NO_RENEGOTIATION_EXTENSION; |
| |
| ssl_info->handshake_type = SSL_session_reused(ssl_) |
| ? SSLInfo::HANDSHAKE_RESUME |
| : SSLInfo::HANDSHAKE_FULL; |
| |
| return true; |
| } |
| |
| void SSLServerSocketImpl::GetConnectionAttempts(ConnectionAttempts* out) const { |
| out->clear(); |
| } |
| |
| int64_t SSLServerSocketImpl::GetTotalReceivedBytes() const { |
| return transport_socket_->GetTotalReceivedBytes(); |
| } |
| |
| void SSLServerSocketImpl::OnSendComplete(int result) { |
| if (next_handshake_state_ == STATE_HANDSHAKE) { |
| // In handshake phase. |
| OnHandshakeIOComplete(result); |
| return; |
| } |
| |
| // TODO(byungchul): This state machine is not correct. Copy the state machine |
| // of SSLClientSocketImpl::OnSendComplete() which handles it better. |
| if (!completed_handshake_) |
| return; |
| |
| if (user_write_buf_) { |
| int rv = DoWriteLoop(result); |
| if (rv != ERR_IO_PENDING) |
| DoWriteCallback(rv); |
| } else { |
| // Ensure that any queued ciphertext is flushed. |
| DoTransportIO(); |
| } |
| } |
| |
| void SSLServerSocketImpl::OnRecvComplete(int result) { |
| if (next_handshake_state_ == STATE_HANDSHAKE) { |
| // In handshake phase. |
| OnHandshakeIOComplete(result); |
| return; |
| } |
| |
| // Network layer received some data, check if client requested to read |
| // decrypted data. |
| if (!user_read_buf_ || !completed_handshake_) |
| return; |
| |
| int rv = DoReadLoop(result); |
| if (rv != ERR_IO_PENDING) |
| DoReadCallback(rv); |
| } |
| |
| void SSLServerSocketImpl::OnHandshakeIOComplete(int result) { |
| int rv = DoHandshakeLoop(result); |
| if (rv == ERR_IO_PENDING) |
| return; |
| |
| net_log_.EndEventWithNetErrorCode(NetLog::TYPE_SSL_SERVER_HANDSHAKE, rv); |
| if (!user_handshake_callback_.is_null()) |
| DoHandshakeCallback(rv); |
| } |
| |
| // Return 0 for EOF, |
| // > 0 for bytes transferred immediately, |
| // < 0 for error (or the non-error ERR_IO_PENDING). |
| int SSLServerSocketImpl::BufferSend() { |
| if (transport_send_busy_) |
| return ERR_IO_PENDING; |
| |
| if (!send_buffer_) { |
| // Get a fresh send buffer out of the send BIO. |
| size_t max_read = BIO_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(&SSLServerSocketImpl::BufferSendComplete, |
| base::Unretained(this))); |
| if (rv == ERR_IO_PENDING) { |
| transport_send_busy_ = true; |
| } else { |
| TransportWriteComplete(rv); |
| } |
| return rv; |
| } |
| |
| void SSLServerSocketImpl::BufferSendComplete(int result) { |
| transport_send_busy_ = false; |
| TransportWriteComplete(result); |
| OnSendComplete(result); |
| } |
| |
| void SSLServerSocketImpl::TransportWriteComplete(int result) { |
| DCHECK(ERR_IO_PENDING != result); |
| if (result < 0) { |
| // Got a socket write error; close the BIO to indicate this upward. |
| // |
| // TODO(davidben): The value of |result| gets lost. Feed the error back into |
| // the BIO so it gets (re-)detected in OnSendComplete. Perhaps with |
| // BIO_set_callback. |
| DVLOG(1) << "TransportWriteComplete error " << result; |
| (void)BIO_shutdown_wr(SSL_get_wbio(ssl_)); |
| |
| // Match the fix for http://crbug.com/249848 in NSS by erroring future reads |
| // from the socket after a write error. |
| // |
| // TODO(davidben): Avoid having read and write ends interact this way. |
| transport_write_error_ = result; |
| (void)BIO_shutdown_wr(transport_bio_); |
| send_buffer_ = NULL; |
| } else { |
| DCHECK(send_buffer_); |
| send_buffer_->DidConsume(result); |
| DCHECK_GE(send_buffer_->BytesRemaining(), 0); |
| if (send_buffer_->BytesRemaining() <= 0) |
| send_buffer_ = NULL; |
| } |
| } |
| |
| int SSLServerSocketImpl::BufferRecv() { |
| 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(&SSLServerSocketImpl::BufferRecvComplete, |
| base::Unretained(this))); |
| if (rv == ERR_IO_PENDING) { |
| transport_recv_busy_ = true; |
| } else { |
| rv = TransportReadComplete(rv); |
| } |
| return rv; |
| } |
| |
| void SSLServerSocketImpl::BufferRecvComplete(int result) { |
| result = TransportReadComplete(result); |
| OnRecvComplete(result); |
| } |
| |
| int SSLServerSocketImpl::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; |
| (void)BIO_shutdown_wr(transport_bio_); |
| } else if (transport_write_error_ < 0) { |
| // Mirror transport write errors as read failures; transport_bio_ has been |
| // shut down by TransportWriteComplete, so the BIO_write will fail, failing |
| // the CHECK. http://crbug.com/335557. |
| result = transport_write_error_; |
| } else { |
| DCHECK(recv_buffer_); |
| int ret = BIO_write(transport_bio_, recv_buffer_->data(), result); |
| // A write into a memory BIO should always succeed. |
| DCHECK_EQ(result, ret); |
| } |
| recv_buffer_ = NULL; |
| transport_recv_busy_ = false; |
| return result; |
| } |
| |
| // Do as much network I/O as possible between the buffer and the |
| // transport socket. Return true if some I/O performed, false |
| // otherwise (error or ERR_IO_PENDING). |
| bool SSLServerSocketImpl::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 SSLServerSocketImpl::DoPayloadRead() { |
| DCHECK(user_read_buf_); |
| DCHECK_GT(user_read_buf_len_, 0); |
| crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); |
| int rv = SSL_read(ssl_, user_read_buf_->data(), user_read_buf_len_); |
| if (rv >= 0) |
| return rv; |
| int ssl_error = SSL_get_error(ssl_, rv); |
| OpenSSLErrorInfo error_info; |
| int net_error = |
| MapOpenSSLErrorWithDetails(ssl_error, err_tracer, &error_info); |
| if (net_error != ERR_IO_PENDING) { |
| net_log_.AddEvent( |
| NetLog::TYPE_SSL_READ_ERROR, |
| CreateNetLogOpenSSLErrorCallback(net_error, ssl_error, error_info)); |
| } |
| return net_error; |
| } |
| |
| int SSLServerSocketImpl::DoPayloadWrite() { |
| DCHECK(user_write_buf_); |
| crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); |
| int rv = SSL_write(ssl_, user_write_buf_->data(), user_write_buf_len_); |
| if (rv >= 0) |
| return rv; |
| int ssl_error = SSL_get_error(ssl_, rv); |
| OpenSSLErrorInfo error_info; |
| int net_error = |
| MapOpenSSLErrorWithDetails(ssl_error, err_tracer, &error_info); |
| if (net_error != ERR_IO_PENDING) { |
| net_log_.AddEvent( |
| NetLog::TYPE_SSL_WRITE_ERROR, |
| CreateNetLogOpenSSLErrorCallback(net_error, ssl_error, error_info)); |
| } |
| return net_error; |
| } |
| |
| int SSLServerSocketImpl::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_NONE: |
| default: |
| rv = ERR_UNEXPECTED; |
| LOG(DFATAL) << "unexpected state " << state; |
| break; |
| } |
| |
| // Do the actual network I/O |
| 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; |
| } |
| |
| int SSLServerSocketImpl::DoReadLoop(int result) { |
| DCHECK(completed_handshake_); |
| DCHECK(next_handshake_state_ == STATE_NONE); |
| |
| if (result < 0) |
| return result; |
| |
| bool network_moved; |
| int rv; |
| do { |
| rv = DoPayloadRead(); |
| network_moved = DoTransportIO(); |
| } while (rv == ERR_IO_PENDING && network_moved); |
| return rv; |
| } |
| |
| int SSLServerSocketImpl::DoWriteLoop(int result) { |
| DCHECK(completed_handshake_); |
| DCHECK_EQ(next_handshake_state_, STATE_NONE); |
| |
| if (result < 0) |
| return result; |
| |
| bool network_moved; |
| int rv; |
| do { |
| rv = DoPayloadWrite(); |
| network_moved = DoTransportIO(); |
| } while (rv == ERR_IO_PENDING && network_moved); |
| return rv; |
| } |
| |
| int SSLServerSocketImpl::DoHandshake() { |
| crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); |
| int net_error = OK; |
| int rv = SSL_do_handshake(ssl_); |
| |
| if (rv == 1) { |
| completed_handshake_ = true; |
| // The results of SSL_get_peer_certificate() must be explicitly freed. |
| ScopedX509 cert(SSL_get_peer_certificate(ssl_)); |
| if (cert) { |
| // The caller does not take ownership of SSL_get_peer_cert_chain's |
| // results. |
| STACK_OF(X509)* chain = SSL_get_peer_cert_chain(ssl_); |
| client_cert_ = CreateX509Certificate(cert.get(), chain); |
| if (!client_cert_.get()) |
| return ERR_SSL_CLIENT_AUTH_CERT_BAD_FORMAT; |
| } |
| } else { |
| int ssl_error = SSL_get_error(ssl_, rv); |
| OpenSSLErrorInfo error_info; |
| net_error = MapOpenSSLErrorWithDetails(ssl_error, err_tracer, &error_info); |
| |
| // This hack is necessary because the mapping of SSL error codes to |
| // net_errors assumes (correctly for client sockets, but erroneously for |
| // server sockets) that peer cert verification failure can only occur if |
| // the cert changed during a renego. crbug.com/570351 |
| if (net_error == ERR_SSL_SERVER_CERT_CHANGED) |
| net_error = ERR_BAD_SSL_CLIENT_AUTH_CERT; |
| |
| // 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, |
| CreateNetLogOpenSSLErrorCallback(net_error, ssl_error, error_info)); |
| } |
| } |
| return net_error; |
| } |
| |
| void SSLServerSocketImpl::DoHandshakeCallback(int rv) { |
| DCHECK_NE(rv, ERR_IO_PENDING); |
| base::ResetAndReturn(&user_handshake_callback_).Run(rv > OK ? OK : rv); |
| } |
| |
| void SSLServerSocketImpl::DoReadCallback(int rv) { |
| DCHECK(rv != ERR_IO_PENDING); |
| DCHECK(!user_read_callback_.is_null()); |
| |
| user_read_buf_ = NULL; |
| user_read_buf_len_ = 0; |
| base::ResetAndReturn(&user_read_callback_).Run(rv); |
| } |
| |
| void SSLServerSocketImpl::DoWriteCallback(int rv) { |
| DCHECK(rv != ERR_IO_PENDING); |
| DCHECK(!user_write_callback_.is_null()); |
| |
| user_write_buf_ = NULL; |
| user_write_buf_len_ = 0; |
| base::ResetAndReturn(&user_write_callback_).Run(rv); |
| } |
| |
| int SSLServerSocketImpl::Init() { |
| DCHECK(!transport_bio_); |
| |
| crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); |
| |
| if (!ssl_) |
| return ERR_UNEXPECTED; |
| |
| BIO* ssl_bio = NULL; |
| // 0 => use default buffer sizes. |
| if (!BIO_new_bio_pair(&ssl_bio, 0, &transport_bio_, 0)) |
| return ERR_UNEXPECTED; |
| DCHECK(ssl_bio); |
| DCHECK(transport_bio_); |
| |
| SSL_set_bio(ssl_, ssl_bio, ssl_bio); |
| |
| return OK; |
| } |
| |
| // static |
| int SSLServerSocketImpl::CertVerifyCallback(X509_STORE_CTX* store_ctx, |
| void* arg) { |
| ClientCertVerifier* verifier = reinterpret_cast<ClientCertVerifier*>(arg); |
| // If a verifier was not supplied, all certificates are accepted. |
| if (!verifier) |
| return 1; |
| STACK_OF(X509)* chain = store_ctx->untrusted; |
| scoped_refptr<X509Certificate> client_cert( |
| CreateX509Certificate(nullptr, chain)); |
| if (!client_cert.get()) { |
| X509_STORE_CTX_set_error(store_ctx, X509_V_ERR_CERT_REJECTED); |
| return 0; |
| } |
| // Asynchronous completion of Verify is currently not supported. |
| // http://crbug.com/347402 |
| // The API for Verify supports the parts needed for async completion |
| // but is currently expected to complete synchronously. |
| std::unique_ptr<ClientCertVerifier::Request> ignore_async; |
| int res = |
| verifier->Verify(client_cert.get(), CompletionCallback(), &ignore_async); |
| DCHECK_NE(res, ERR_IO_PENDING); |
| |
| if (res != OK) { |
| X509_STORE_CTX_set_error(store_ctx, X509_V_ERR_CERT_REJECTED); |
| return 0; |
| } |
| return 1; |
| } |
| |
| } // namespace |
| |
| std::unique_ptr<SSLServerContext> CreateSSLServerContext( |
| X509Certificate* certificate, |
| const crypto::RSAPrivateKey& key, |
| const SSLServerConfig& ssl_server_config) { |
| return std::unique_ptr<SSLServerContext>( |
| new SSLServerContextImpl(certificate, key, ssl_server_config)); |
| } |
| |
| SSLServerContextImpl::SSLServerContextImpl( |
| X509Certificate* certificate, |
| const crypto::RSAPrivateKey& key, |
| const SSLServerConfig& ssl_server_config) |
| : ssl_server_config_(ssl_server_config), |
| cert_(certificate), |
| key_(key.Copy()) { |
| CHECK(key_); |
| crypto::EnsureOpenSSLInit(); |
| ssl_ctx_.reset(SSL_CTX_new(TLS_method())); |
| SSL_CTX_set_session_cache_mode(ssl_ctx_.get(), SSL_SESS_CACHE_SERVER); |
| uint8_t session_ctx_id = 0; |
| SSL_CTX_set_session_id_context(ssl_ctx_.get(), &session_ctx_id, |
| sizeof(session_ctx_id)); |
| |
| int verify_mode = 0; |
| switch (ssl_server_config_.client_cert_type) { |
| case SSLServerConfig::ClientCertType::REQUIRE_CLIENT_CERT: |
| verify_mode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT; |
| // Fall-through |
| case SSLServerConfig::ClientCertType::OPTIONAL_CLIENT_CERT: |
| verify_mode |= SSL_VERIFY_PEER; |
| SSL_CTX_set_verify(ssl_ctx_.get(), verify_mode, nullptr); |
| SSL_CTX_set_cert_verify_callback(ssl_ctx_.get(), |
| SSLServerSocketImpl::CertVerifyCallback, |
| ssl_server_config_.client_cert_verifier); |
| break; |
| case SSLServerConfig::ClientCertType::NO_CLIENT_CERT: |
| break; |
| } |
| |
| // Set certificate and private key. |
| DCHECK(cert_->os_cert_handle()); |
| #if defined(USE_OPENSSL_CERTS) |
| CHECK(SSL_CTX_use_certificate(ssl_ctx_.get(), cert_->os_cert_handle())); |
| #else |
| // Convert OSCertHandle to X509 structure. |
| std::string der_string; |
| CHECK(X509Certificate::GetDEREncoded(cert_->os_cert_handle(), &der_string)); |
| |
| const unsigned char* der_string_array = |
| reinterpret_cast<const unsigned char*>(der_string.data()); |
| |
| ScopedX509 x509(d2i_X509(NULL, &der_string_array, der_string.length())); |
| CHECK(x509); |
| |
| // On success, SSL_CTX_use_certificate acquires a reference to |x509|. |
| CHECK(SSL_CTX_use_certificate(ssl_ctx_.get(), x509.get())); |
| #endif // USE_OPENSSL_CERTS |
| |
| DCHECK(key_->key()); |
| CHECK(SSL_CTX_use_PrivateKey(ssl_ctx_.get(), key_->key())); |
| |
| DCHECK_LT(SSL3_VERSION, ssl_server_config_.version_min); |
| DCHECK_LT(SSL3_VERSION, ssl_server_config_.version_max); |
| SSL_CTX_set_min_version(ssl_ctx_.get(), ssl_server_config_.version_min); |
| SSL_CTX_set_max_version(ssl_ctx_.get(), ssl_server_config_.version_max); |
| |
| // 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_COMPRESSION, true); |
| |
| SSL_CTX_set_options(ssl_ctx_.get(), options.set_mask); |
| SSL_CTX_clear_options(ssl_ctx_.get(), options.clear_mask); |
| |
| // Same as above, this time for the SSL mode. |
| SslSetClearMask mode; |
| |
| mode.ConfigureFlag(SSL_MODE_RELEASE_BUFFERS, true); |
| |
| SSL_CTX_set_mode(ssl_ctx_.get(), mode.set_mask); |
| SSL_CTX_clear_mode(ssl_ctx_.get(), mode.clear_mask); |
| |
| // See SSLServerConfig::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:!SHA256:!SHA384:!AESGCM+AES256:!aPSK"); |
| |
| if (ssl_server_config_.require_ecdhe) |
| command.append(":!kRSA:!kDHE"); |
| |
| // Remove any disabled ciphers. |
| for (uint16_t id : ssl_server_config_.disabled_cipher_suites) { |
| const SSL_CIPHER* cipher = SSL_get_cipher_by_value(id); |
| if (cipher) { |
| command.append(":!"); |
| command.append(SSL_CIPHER_get_name(cipher)); |
| } |
| } |
| |
| int rv = SSL_CTX_set_cipher_list(ssl_ctx_.get(), 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; |
| |
| if (ssl_server_config_.client_cert_type != |
| SSLServerConfig::ClientCertType::NO_CLIENT_CERT && |
| !ssl_server_config_.cert_authorities_.empty()) { |
| ScopedX509NameStack stack(sk_X509_NAME_new_null()); |
| for (const auto& authority : ssl_server_config_.cert_authorities_) { |
| const uint8_t* name = reinterpret_cast<const uint8_t*>(authority.c_str()); |
| const uint8_t* name_start = name; |
| ScopedX509_NAME subj(d2i_X509_NAME(nullptr, &name, authority.length())); |
| CHECK(subj && name == name_start + authority.length()); |
| sk_X509_NAME_push(stack.get(), subj.release()); |
| } |
| SSL_CTX_set_client_CA_list(ssl_ctx_.get(), stack.release()); |
| } |
| } |
| |
| SSLServerContextImpl::~SSLServerContextImpl() {} |
| |
| std::unique_ptr<SSLServerSocket> SSLServerContextImpl::CreateSSLServerSocket( |
| std::unique_ptr<StreamSocket> socket) { |
| SSL* ssl = SSL_new(ssl_ctx_.get()); |
| return std::unique_ptr<SSLServerSocket>( |
| new SSLServerSocketImpl(std::move(socket), ssl)); |
| } |
| |
| void EnableSSLServerSockets() { |
| // No-op because CreateSSLServerSocket() calls crypto::EnsureOpenSSLInit(). |
| } |
| |
| } // namespace net |