Index: net/socket/ssl_server_socket_openssl.cc |
diff --git a/net/socket/ssl_server_socket_openssl.cc b/net/socket/ssl_server_socket_openssl.cc |
index c327f2caf10e4e179c5295f17b80dc47c1d8600a..9d6f7ec2950bd1433830778956fd43036e31284f 100644 |
--- a/net/socket/ssl_server_socket_openssl.cc |
+++ b/net/socket/ssl_server_socket_openssl.cc |
@@ -2,18 +2,42 @@ |
// 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_openssl.h" |
+ |
+#include <openssl/err.h> |
+#include <openssl/ssl.h> |
+ |
+#include "base/debug/alias.h" |
#include "base/logging.h" |
-#include "net/socket/ssl_server_socket.h" |
+#include "crypto/openssl_util.h" |
+#include "crypto/rsa_private_key.h" |
+#include "net/base/net_errors.h" |
+#include "net/socket/openssl_util.h" |
+#include "net/socket/ssl_error_params.h" |
-// TODO(bulach): Provide simple stubs for EnableSSLServerSockets and |
-// CreateSSLServerSocket so that when building for OpenSSL rather than NSS, |
-// so that the code using SSL server sockets can be compiled and disabled |
-// programatically rather than requiring to be carved out from the compile. |
+// Enable this to see logging for state machine state transitions. |
+#if 0 |
+#define GotoState(s) do { DVLOG(2) << (void *)this << " " << __FUNCTION__ << \ |
+ " jump to state " << s; \ |
+ next_handshake_state_ = s; } while (0) |
+#else |
+#define GotoState(s) next_handshake_state_ = s |
+#endif |
Ryan Sleevi
2014/05/12 01:35:25
Remove this
byungchul
2014/05/12 18:25:23
Done.
|
namespace net { |
+namespace { |
+ |
+bool g_openssl_server_sockets_init = false; |
Ryan Sleevi
2014/05/12 01:35:25
This is not thread-safe. Use LazyInstance for thre
byungchul
2014/05/12 18:25:23
Done.
|
+ |
+} // namespace |
+ |
+ |
void EnableSSLServerSockets() { |
- NOTIMPLEMENTED(); |
+ if (!g_openssl_server_sockets_init) { |
+ crypto::EnsureOpenSSLInit(); |
+ g_openssl_server_sockets_init = true; |
+ } |
} |
scoped_ptr<SSLServerSocket> CreateSSLServerSocket( |
@@ -21,8 +45,638 @@ scoped_ptr<SSLServerSocket> CreateSSLServerSocket( |
X509Certificate* certificate, |
crypto::RSAPrivateKey* key, |
const SSLConfig& ssl_config) { |
+ DCHECK(g_openssl_server_sockets_init) |
+ << "EnableSSLServerSockets() has not been called yet!"; |
Ryan Sleevi
2014/05/12 01:35:25
Remove this
byungchul
2014/05/12 18:25:23
Done.
|
+ |
+ return scoped_ptr<SSLServerSocket>( |
+ new SSLServerSocketOpenSSL(socket.Pass(), certificate, key, ssl_config)); |
+} |
+ |
+SSLServerSocketOpenSSL::SSLServerSocketOpenSSL( |
+ scoped_ptr<StreamSocket> transport_socket, |
+ scoped_refptr<X509Certificate> certificate, |
+ crypto::RSAPrivateKey* key, |
+ const SSLConfig& ssl_config) |
+ : 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_(NULL), |
+ transport_bio_(NULL), |
+ transport_socket_(transport_socket.Pass()), |
+ ssl_config_(ssl_config), |
+ cert_(certificate), |
+ next_handshake_state_(STATE_NONE), |
+ completed_handshake_(false) { |
+ // TODO(byungchul): Need a better way to clone a key. |
+ std::vector<uint8> key_bytes; |
+ CHECK(key->ExportPrivateKey(&key_bytes)); |
+ key_.reset(crypto::RSAPrivateKey::CreateFromPrivateKeyInfo(key_bytes)); |
+ CHECK(key_.get()); |
+} |
+ |
+SSLServerSocketOpenSSL::~SSLServerSocketOpenSSL() { |
+ 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 SSLServerSocketOpenSSL::Handshake(const CompletionCallback& callback) { |
+ net_log_.BeginEvent(NetLog::TYPE_SSL_SERVER_HANDSHAKE); |
+ |
+ // Set up new ssl object. |
+ if (!Init()) { |
+ LOG(ERROR) << "Failed to initialize OpenSSL"; |
+ int rv = ERR_UNEXPECTED; |
+ 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); |
+ int 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 SSLServerSocketOpenSSL::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; |
+ if (SSL_export_keying_material( |
+ ssl_, out, outlen, label.data(), label.size(), |
+ reinterpret_cast<const unsigned char*>(context.data()), |
+ context.length(), has_context ? 1 : 0) == 0) { |
+ LOG(ERROR) << "SSL_export_keying_material error"; |
+ return ERR_FAILED; |
+ } |
+ return OK; |
+} |
+ |
+int SSLServerSocketOpenSSL::GetTLSUniqueChannelBinding(std::string* out) { |
+ return ERR_NOT_IMPLEMENTED; |
+} |
+ |
+int SSLServerSocketOpenSSL::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_.get()); |
+ 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 SSLServerSocketOpenSSL::Write(IOBuffer* buf, int buf_len, |
+ const CompletionCallback& callback) { |
+ DCHECK(user_write_callback_.is_null()); |
+ DCHECK(!user_write_buf_.get()); |
+ 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 SSLServerSocketOpenSSL::SetReceiveBufferSize(int32 size) { |
+ return transport_socket_->SetReceiveBufferSize(size); |
+} |
+ |
+int SSLServerSocketOpenSSL::SetSendBufferSize(int32 size) { |
+ return transport_socket_->SetSendBufferSize(size); |
+} |
+ |
+int SSLServerSocketOpenSSL::Connect(const CompletionCallback& callback) { |
NOTIMPLEMENTED(); |
- return scoped_ptr<SSLServerSocket>(); |
+ return ERR_NOT_IMPLEMENTED; |
+} |
+ |
+void SSLServerSocketOpenSSL::Disconnect() { |
+ transport_socket_->Disconnect(); |
+} |
+ |
+bool SSLServerSocketOpenSSL::IsConnected() const { |
+ return completed_handshake_; |
+} |
+ |
+bool SSLServerSocketOpenSSL::IsConnectedAndIdle() const { |
+ return completed_handshake_ && transport_socket_->IsConnectedAndIdle(); |
+} |
+ |
+int SSLServerSocketOpenSSL::GetPeerAddress(IPEndPoint* address) const { |
+ if (!IsConnected()) |
+ return ERR_SOCKET_NOT_CONNECTED; |
+ return transport_socket_->GetPeerAddress(address); |
+} |
+ |
+int SSLServerSocketOpenSSL::GetLocalAddress(IPEndPoint* address) const { |
+ if (!IsConnected()) |
+ return ERR_SOCKET_NOT_CONNECTED; |
+ return transport_socket_->GetLocalAddress(address); |
+} |
+ |
+const BoundNetLog& SSLServerSocketOpenSSL::NetLog() const { |
+ return net_log_; |
+} |
+ |
+void SSLServerSocketOpenSSL::SetSubresourceSpeculation() { |
+ transport_socket_->SetSubresourceSpeculation(); |
+} |
+ |
+void SSLServerSocketOpenSSL::SetOmniboxSpeculation() { |
+ transport_socket_->SetOmniboxSpeculation(); |
+} |
+ |
+bool SSLServerSocketOpenSSL::WasEverUsed() const { |
+ return transport_socket_->WasEverUsed(); |
+} |
+ |
+bool SSLServerSocketOpenSSL::UsingTCPFastOpen() const { |
+ return transport_socket_->UsingTCPFastOpen(); |
+} |
+ |
+bool SSLServerSocketOpenSSL::WasNpnNegotiated() const { |
+ return false; |
+} |
+ |
+NextProto SSLServerSocketOpenSSL::GetNegotiatedProtocol() const { |
+ // NPN is not supported by this class. |
+ return kProtoUnknown; |
+} |
+ |
+bool SSLServerSocketOpenSSL::GetSSLInfo(SSLInfo* ssl_info) { |
+ NOTIMPLEMENTED(); |
+ return false; |
+} |
+ |
+void SSLServerSocketOpenSSL::OnSendComplete(int result) { |
+ if (next_handshake_state_ == STATE_HANDSHAKE) { |
+ // In handshake phase. |
+ OnHandshakeIOComplete(result); |
+ return; |
+ } |
+ |
+ if (!completed_handshake_) |
+ return; |
+ |
+ if (user_write_buf_.get()) { |
+ int rv = DoWriteLoop(result); |
+ if (rv != ERR_IO_PENDING) |
+ DoWriteCallback(rv); |
+ } else { |
+ // Ensure that any queued ciphertext is flushed. |
+ DoTransportIO(); |
+ } |
Ryan Sleevi
2014/05/12 01:35:25
This strikes me as a bad state machine (perhaps in
byungchul
2014/05/12 18:25:23
These code here and below are imported from ssl_se
|
+} |
+ |
+void SSLServerSocketOpenSSL::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_.get() || !completed_handshake_) |
+ return; |
+ |
+ int rv = DoReadLoop(result); |
+ if (rv != ERR_IO_PENDING) |
+ DoReadCallback(rv); |
+} |
+ |
+void SSLServerSocketOpenSSL::OnHandshakeIOComplete(int result) { |
+ int rv = DoHandshakeLoop(result); |
+ if (rv != ERR_IO_PENDING) { |
Ryan Sleevi
2014/05/12 01:35:25
better to be
if (rv == ERR_IO_PENDING)
return
n
byungchul
2014/05/12 18:25:23
Done.
|
+ 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 SSLServerSocketOpenSSL::BufferSend() { |
+ 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(&SSLServerSocketOpenSSL::BufferSendComplete, |
+ base::Unretained(this))); |
+ if (rv == ERR_IO_PENDING) { |
+ transport_send_busy_ = true; |
+ } else { |
+ TransportWriteComplete(rv); |
+ } |
+ return rv; |
+} |
+ |
+void SSLServerSocketOpenSSL::BufferSendComplete(int result) { |
+ transport_send_busy_ = false; |
+ TransportWriteComplete(result); |
+ OnSendComplete(result); |
+} |
+ |
+void SSLServerSocketOpenSSL::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_.get()); |
+ send_buffer_->DidConsume(result); |
+ DCHECK_GE(send_buffer_->BytesRemaining(), 0); |
+ if (send_buffer_->BytesRemaining() <= 0) |
+ send_buffer_ = NULL; |
+ } |
+} |
+ |
+int SSLServerSocketOpenSSL::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(&SSLServerSocketOpenSSL::BufferRecvComplete, |
+ base::Unretained(this))); |
+ if (rv == ERR_IO_PENDING) { |
+ transport_recv_busy_ = true; |
+ } else { |
+ rv = TransportReadComplete(rv); |
+ } |
+ return rv; |
+} |
+ |
+void SSLServerSocketOpenSSL::BufferRecvComplete(int result) { |
+ result = TransportReadComplete(result); |
+ OnRecvComplete(result); |
+} |
+ |
+int SSLServerSocketOpenSSL::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_.get()); |
+ int ret = BIO_write(transport_bio_, recv_buffer_->data(), result); |
+ // A write into a memory BIO should always succeed. |
+ // Force values on the stack for http://crbug.com/335557 |
+ base::debug::Alias(&result); |
+ base::debug::Alias(&ret); |
+ CHECK_EQ(result, ret); |
Ryan Sleevi
2014/05/12 01:35:25
crbug.com/335557 was resolved. This code should be
byungchul
2014/05/12 18:25:23
Done.
|
+ } |
+ 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 SSLServerSocketOpenSSL::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 SSLServerSocketOpenSSL::DoPayloadRead() { |
+ DCHECK(user_read_buf_.get()); |
+ 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); |
+ int net_error = MapOpenSSLError(ssl_error, err_tracer); |
+ if (net_error != ERR_IO_PENDING) { |
+ net_log_.AddEvent(NetLog::TYPE_SSL_READ_ERROR, |
+ CreateNetLogSSLErrorCallback(net_error, ssl_error)); |
+ } |
+ return net_error; |
+} |
+ |
+int SSLServerSocketOpenSSL::DoPayloadWrite() { |
+ DCHECK(user_write_buf_.get()); |
+ 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); |
+ int net_error = MapOpenSSLError(ssl_error, err_tracer); |
+ if (net_error != ERR_IO_PENDING) { |
+ net_log_.AddEvent(NetLog::TYPE_SSL_WRITE_ERROR, |
+ CreateNetLogSSLErrorCallback(net_error, ssl_error)); |
+ } |
+ return net_error; |
+} |
+ |
+int SSLServerSocketOpenSSL::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 SSLServerSocketOpenSSL::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 SSLServerSocketOpenSSL::DoWriteLoop(int result) { |
+ DCHECK(completed_handshake_); |
+ DCHECK(next_handshake_state_ == STATE_NONE); |
Ryan Sleevi
2014/05/12 01:35:25
DCHECK_EQ
byungchul
2014/05/12 18:25:23
Done.
|
+ |
+ 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 SSLServerSocketOpenSSL::DoHandshake() { |
+ crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); |
+ int net_error = net::OK; |
+ int rv = SSL_do_handshake(ssl_); |
+ |
+ if (rv == 1) { |
+ completed_handshake_ = true; |
+ } else { |
+ int ssl_error = SSL_get_error(ssl_, rv); |
+ 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; |
+} |
+ |
+void SSLServerSocketOpenSSL::DoHandshakeCallback(int rv) { |
+ DCHECK_NE(rv, ERR_IO_PENDING); |
+ |
+ CompletionCallback c = user_handshake_callback_; |
+ user_handshake_callback_.Reset(); |
+ c.Run(rv > OK ? OK : rv); |
Ryan Sleevi
2014/05/12 01:35:25
Use base::ResetAndReturn() here (base/callback_hel
byungchul
2014/05/12 18:25:23
Done.
|
+} |
+ |
+void SSLServerSocketOpenSSL::DoReadCallback(int rv) { |
+ DCHECK(rv != ERR_IO_PENDING); |
+ DCHECK(!user_read_callback_.is_null()); |
+ |
+ // Since Run may result in Read being called, clear |user_read_callback_| |
+ // up front. |
+ CompletionCallback c = user_read_callback_; |
+ user_read_callback_.Reset(); |
+ user_read_buf_ = NULL; |
+ user_read_buf_len_ = 0; |
+ c.Run(rv); |
Ryan Sleevi
2014/05/12 01:35:25
base::ResetAndReturn here
byungchul
2014/05/12 18:25:23
Done.
|
+} |
+ |
+void SSLServerSocketOpenSSL::DoWriteCallback(int rv) { |
+ DCHECK(rv != ERR_IO_PENDING); |
+ DCHECK(!user_write_callback_.is_null()); |
+ |
+ // Since Run may result in Write being called, clear |user_write_callback_| |
+ // up front. |
+ CompletionCallback c = user_write_callback_; |
+ user_write_callback_.Reset(); |
+ user_write_buf_ = NULL; |
+ user_write_buf_len_ = 0; |
+ c.Run(rv); |
Ryan Sleevi
2014/05/12 01:35:25
base::ResetAndReturn here
byungchul
2014/05/12 18:25:23
Done.
|
+} |
+ |
+bool SSLServerSocketOpenSSL::Init() { |
+ DCHECK(!ssl_); |
+ DCHECK(!transport_bio_); |
+ |
+ crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); |
+ |
+ crypto::ScopedOpenSSL<SSL_CTX, SSL_CTX_free> ssl_ctx( |
+ // It support SSLv2, SSLv3, and TLSv1. |
+ SSL_CTX_new(SSLv23_server_method())); |
+ ssl_ = SSL_new(ssl_ctx.get()); |
+ if (!ssl_) |
+ return false; |
+ |
+ 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_set_bio(ssl_, ssl_bio, ssl_bio); |
+ |
+ // Set certificate and private key. |
+ DCHECK(cert_->os_cert_handle()); |
+ if (SSL_use_certificate(ssl_, cert_->os_cert_handle()) != 1) { |
+ LOG(ERROR) << "Cannot set certificate."; |
+ return false; |
+ } |
+ |
+ DCHECK(key_->key()); |
+ if (SSL_use_PrivateKey(ssl_, key_->key()) != 1) { |
+ LOG(ERROR) << "Cannot set private key."; |
+ return false; |
+ } |
+ |
+ // 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); |
+ 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); |
+ 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); |
+ |
+ options.ConfigureFlag(SSL_OP_NO_COMPRESSION, true); |
+ |
+ SSL_set_options(ssl_, options.set_mask); |
+ SSL_clear_options(ssl_, options.clear_mask); |
+ |
+ // Same as above, this time for the SSL mode. |
+ SslSetClearMask mode; |
+ |
+ mode.ConfigureFlag(SSL_MODE_RELEASE_BUFFERS, true); |
+ |
+ SSL_set_mode(ssl_, mode.set_mask); |
+ SSL_clear_mode(ssl_, mode.clear_mask); |
+ |
+ return true; |
} |
} // namespace net |