Index: net/socket/ssl_server_socket_nss.cc |
diff --git a/net/socket/ssl_server_socket_nss.cc b/net/socket/ssl_server_socket_nss.cc |
deleted file mode 100644 |
index 17f7d62fc97e126f787e567cd4df0125e5b3f98f..0000000000000000000000000000000000000000 |
--- a/net/socket/ssl_server_socket_nss.cc |
+++ /dev/null |
@@ -1,982 +0,0 @@ |
-// 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_nss.h" |
- |
-#include <utility> |
- |
-#if defined(OS_WIN) |
-#include <winsock2.h> |
-#endif |
- |
-#if defined(USE_SYSTEM_SSL) |
-#include <dlfcn.h> |
-#endif |
-#if defined(OS_MACOSX) |
-#include <Security/Security.h> |
-#endif |
-#include <certdb.h> |
-#include <cryptohi.h> |
-#include <hasht.h> |
-#include <keyhi.h> |
-#include <nspr.h> |
-#include <nss.h> |
-#include <pk11pub.h> |
-#include <secerr.h> |
-#include <sechash.h> |
-#include <ssl.h> |
-#include <sslerr.h> |
-#include <sslproto.h> |
- |
-#include <limits> |
- |
-#include "base/callback_helpers.h" |
-#include "base/lazy_instance.h" |
-#include "base/logging.h" |
-#include "base/memory/ref_counted.h" |
-#include "crypto/nss_util_internal.h" |
-#include "crypto/rsa_private_key.h" |
-#include "net/base/io_buffer.h" |
-#include "net/base/net_errors.h" |
-#include "net/log/net_log.h" |
-#include "net/socket/nss_ssl_util.h" |
- |
-// SSL plaintext fragments are shorter than 16KB. Although the record layer |
-// overhead is allowed to be 2K + 5 bytes, in practice the overhead is much |
-// smaller than 1KB. So a 17KB buffer should be large enough to hold an |
-// entire SSL record. |
-static const int kRecvBufferSize = 17 * 1024; |
-static const int kSendBufferSize = 17 * 1024; |
- |
-#define GotoState(s) next_handshake_state_ = s |
- |
-namespace net { |
- |
-namespace { |
- |
-bool g_nss_server_sockets_init = false; |
- |
-class NSSSSLServerInitSingleton { |
- public: |
- NSSSSLServerInitSingleton() { |
- EnsureNSSSSLInit(); |
- |
- SSL_ConfigServerSessionIDCache(64, 28800, 28800, NULL); |
- g_nss_server_sockets_init = true; |
- } |
- |
- ~NSSSSLServerInitSingleton() { |
- SSL_ShutdownServerSessionIDCache(); |
- g_nss_server_sockets_init = false; |
- } |
-}; |
- |
-static base::LazyInstance<NSSSSLServerInitSingleton>::Leaky |
- g_nss_ssl_server_init_singleton = LAZY_INSTANCE_INITIALIZER; |
- |
-class SSLServerSocketNSS : public SSLServerSocket { |
- public: |
- // See comments on CreateSSLServerSocket for details of how these |
- // parameters are used. |
- SSLServerSocketNSS(std::unique_ptr<StreamSocket> socket, |
- X509Certificate* certificate, |
- const crypto::RSAPrivateKey& key, |
- const SSLServerConfig& ssl_server_config); |
- ~SSLServerSocketNSS() 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; |
- |
- private: |
- enum State { |
- STATE_NONE, |
- STATE_HANDSHAKE, |
- }; |
- |
- int InitializeSSLOptions(); |
- |
- void OnSendComplete(int result); |
- void OnRecvComplete(int result); |
- void OnHandshakeIOComplete(int result); |
- |
- int BufferSend(); |
- void BufferSendComplete(int result); |
- int BufferRecv(); |
- void BufferRecvComplete(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); |
- |
- static SECStatus OwnAuthCertHandler(void* arg, |
- PRFileDesc* socket, |
- PRBool checksig, |
- PRBool is_server); |
- static void HandshakeCallback(PRFileDesc* socket, void* arg); |
- |
- int Init(); |
- |
- // Members used to send and receive buffer. |
- bool transport_send_busy_; |
- bool transport_recv_busy_; |
- |
- 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_; |
- |
- // The NSS SSL state machine |
- PRFileDesc* nss_fd_; |
- |
- // Buffers for the network end of the SSL state machine |
- memio_Private* nss_bufs_; |
- |
- // StreamSocket for sending and receiving data. |
- std::unique_ptr<StreamSocket> transport_socket_; |
- |
- // Options for the SSL socket. |
- SSLServerConfig ssl_server_config_; |
- |
- // Certificate for the server. |
- scoped_refptr<X509Certificate> cert_; |
- |
- // Private key used by the server. |
- std::unique_ptr<crypto::RSAPrivateKey> key_; |
- |
- State next_handshake_state_; |
- bool completed_handshake_; |
- |
- DISALLOW_COPY_AND_ASSIGN(SSLServerSocketNSS); |
-}; |
- |
-SSLServerSocketNSS::SSLServerSocketNSS( |
- std::unique_ptr<StreamSocket> transport_socket, |
- X509Certificate* cert, |
- const crypto::RSAPrivateKey& key, |
- const SSLServerConfig& ssl_server_config) |
- : transport_send_busy_(false), |
- transport_recv_busy_(false), |
- user_read_buf_len_(0), |
- user_write_buf_len_(0), |
- nss_fd_(NULL), |
- nss_bufs_(NULL), |
- transport_socket_(std::move(transport_socket)), |
- ssl_server_config_(ssl_server_config), |
- cert_(cert), |
- key_(key.Copy()), |
- next_handshake_state_(STATE_NONE), |
- completed_handshake_(false) { |
- CHECK(key_); |
-} |
- |
-SSLServerSocketNSS::~SSLServerSocketNSS() { |
- if (nss_fd_ != NULL) { |
- PR_Close(nss_fd_); |
- nss_fd_ = NULL; |
- } |
-} |
- |
-int SSLServerSocketNSS::Handshake(const CompletionCallback& callback) { |
- net_log_.BeginEvent(NetLog::TYPE_SSL_SERVER_HANDSHAKE); |
- |
- int rv = Init(); |
- if (rv != OK) { |
- LOG(ERROR) << "Failed to initialize NSS"; |
- net_log_.EndEventWithNetErrorCode(NetLog::TYPE_SSL_SERVER_HANDSHAKE, rv); |
- return rv; |
- } |
- |
- rv = InitializeSSLOptions(); |
- if (rv != OK) { |
- LOG(ERROR) << "Failed to initialize SSL options"; |
- net_log_.EndEventWithNetErrorCode(NetLog::TYPE_SSL_SERVER_HANDSHAKE, rv); |
- return rv; |
- } |
- |
- // Set peer address. TODO(hclam): This should be in a separate method. |
- PRNetAddr peername; |
- memset(&peername, 0, sizeof(peername)); |
- peername.raw.family = AF_INET; |
- memio_SetPeerName(nss_fd_, &peername); |
- |
- 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 SSLServerSocketNSS::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; |
- SECStatus result = SSL_ExportKeyingMaterial( |
- nss_fd_, label.data(), label.size(), has_context, |
- reinterpret_cast<const unsigned char*>(context.data()), |
- context.length(), out, outlen); |
- if (result != SECSuccess) { |
- LogFailedNSSFunction(net_log_, "SSL_ExportKeyingMaterial", ""); |
- return MapNSSError(PORT_GetError()); |
- } |
- return OK; |
-} |
- |
-int SSLServerSocketNSS::Connect(const CompletionCallback& callback) { |
- NOTIMPLEMENTED(); |
- return ERR_NOT_IMPLEMENTED; |
-} |
- |
-int SSLServerSocketNSS::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(nss_bufs_); |
- 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 SSLServerSocketNSS::Write(IOBuffer* buf, |
- int buf_len, |
- const CompletionCallback& callback) { |
- DCHECK(user_write_callback_.is_null()); |
- DCHECK(!user_write_buf_); |
- DCHECK(nss_bufs_); |
- 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 SSLServerSocketNSS::SetReceiveBufferSize(int32_t size) { |
- return transport_socket_->SetReceiveBufferSize(size); |
-} |
- |
-int SSLServerSocketNSS::SetSendBufferSize(int32_t size) { |
- return transport_socket_->SetSendBufferSize(size); |
-} |
- |
-bool SSLServerSocketNSS::IsConnected() const { |
- // TODO(wtc): Find out if we should check transport_socket_->IsConnected() |
- // as well. |
- return completed_handshake_; |
-} |
- |
-void SSLServerSocketNSS::Disconnect() { |
- transport_socket_->Disconnect(); |
-} |
- |
-bool SSLServerSocketNSS::IsConnectedAndIdle() const { |
- return completed_handshake_ && transport_socket_->IsConnectedAndIdle(); |
-} |
- |
-int SSLServerSocketNSS::GetPeerAddress(IPEndPoint* address) const { |
- if (!IsConnected()) |
- return ERR_SOCKET_NOT_CONNECTED; |
- return transport_socket_->GetPeerAddress(address); |
-} |
- |
-int SSLServerSocketNSS::GetLocalAddress(IPEndPoint* address) const { |
- if (!IsConnected()) |
- return ERR_SOCKET_NOT_CONNECTED; |
- return transport_socket_->GetLocalAddress(address); |
-} |
- |
-const BoundNetLog& SSLServerSocketNSS::NetLog() const { |
- return net_log_; |
-} |
- |
-void SSLServerSocketNSS::SetSubresourceSpeculation() { |
- transport_socket_->SetSubresourceSpeculation(); |
-} |
- |
-void SSLServerSocketNSS::SetOmniboxSpeculation() { |
- transport_socket_->SetOmniboxSpeculation(); |
-} |
- |
-bool SSLServerSocketNSS::WasEverUsed() const { |
- return transport_socket_->WasEverUsed(); |
-} |
- |
-bool SSLServerSocketNSS::WasNpnNegotiated() const { |
- NOTIMPLEMENTED(); |
- return false; |
-} |
- |
-NextProto SSLServerSocketNSS::GetNegotiatedProtocol() const { |
- // NPN is not supported by this class. |
- return kProtoUnknown; |
-} |
- |
-bool SSLServerSocketNSS::GetSSLInfo(SSLInfo* ssl_info) { |
- NOTIMPLEMENTED(); |
- return false; |
-} |
- |
-void SSLServerSocketNSS::GetConnectionAttempts(ConnectionAttempts* out) const { |
- out->clear(); |
-} |
- |
-int64_t SSLServerSocketNSS::GetTotalReceivedBytes() const { |
- NOTIMPLEMENTED(); |
- return 0; |
-} |
- |
-int SSLServerSocketNSS::InitializeSSLOptions() { |
- // Transport connected, now hook it up to nss |
- nss_fd_ = memio_CreateIOLayer(kRecvBufferSize, kSendBufferSize); |
- if (nss_fd_ == NULL) { |
- return ERR_OUT_OF_MEMORY; // TODO(port): map NSPR error code. |
- } |
- |
- // Grab pointer to buffers |
- nss_bufs_ = memio_GetSecret(nss_fd_); |
- |
- /* Create SSL state machine */ |
- /* Push SSL onto our fake I/O socket */ |
- nss_fd_ = SSL_ImportFD(NULL, nss_fd_); |
- if (nss_fd_ == NULL) { |
- LogFailedNSSFunction(net_log_, "SSL_ImportFD", ""); |
- return ERR_OUT_OF_MEMORY; // TODO(port): map NSPR/NSS error code. |
- } |
- // TODO(port): set more ssl options! Check errors! |
- |
- int rv; |
- |
- if (ssl_server_config_.client_cert_type == |
- SSLServerConfig::ClientCertType::REQUIRE_CLIENT_CERT) { |
- rv = SSL_OptionSet(nss_fd_, SSL_REQUEST_CERTIFICATE, PR_TRUE); |
- if (rv != SECSuccess) { |
- LogFailedNSSFunction(net_log_, "SSL_OptionSet", |
- "SSL_REQUEST_CERTIFICATE"); |
- return ERR_UNEXPECTED; |
- } |
- } |
- |
- rv = SSL_OptionSet(nss_fd_, SSL_SECURITY, PR_TRUE); |
- if (rv != SECSuccess) { |
- LogFailedNSSFunction(net_log_, "SSL_OptionSet", "SSL_SECURITY"); |
- return ERR_UNEXPECTED; |
- } |
- |
- rv = SSL_OptionSet(nss_fd_, SSL_ENABLE_SSL2, PR_FALSE); |
- if (rv != SECSuccess) { |
- LogFailedNSSFunction(net_log_, "SSL_OptionSet", "SSL_ENABLE_SSL2"); |
- return ERR_UNEXPECTED; |
- } |
- |
- SSLVersionRange version_range; |
- version_range.min = ssl_server_config_.version_min; |
- version_range.max = ssl_server_config_.version_max; |
- rv = SSL_VersionRangeSet(nss_fd_, &version_range); |
- if (rv != SECSuccess) { |
- LogFailedNSSFunction(net_log_, "SSL_VersionRangeSet", ""); |
- return ERR_NO_SSL_VERSIONS_ENABLED; |
- } |
- |
- if (ssl_server_config_.require_ecdhe) { |
- const PRUint16* const ssl_ciphers = SSL_GetImplementedCiphers(); |
- const PRUint16 num_ciphers = SSL_GetNumImplementedCiphers(); |
- |
- // Iterate over the cipher suites and disable those that don't use ECDHE. |
- for (unsigned i = 0; i < num_ciphers; i++) { |
- SSLCipherSuiteInfo info; |
- if (SSL_GetCipherSuiteInfo(ssl_ciphers[i], &info, sizeof(info)) == |
- SECSuccess) { |
- if (strcmp(info.keaTypeName, "ECDHE") != 0) { |
- SSL_CipherPrefSet(nss_fd_, ssl_ciphers[i], PR_FALSE); |
- } |
- } |
- } |
- } |
- |
- for (std::vector<uint16_t>::const_iterator it = |
- ssl_server_config_.disabled_cipher_suites.begin(); |
- it != ssl_server_config_.disabled_cipher_suites.end(); ++it) { |
- // This will fail if the specified cipher is not implemented by NSS, but |
- // the failure is harmless. |
- SSL_CipherPrefSet(nss_fd_, *it, PR_FALSE); |
- } |
- |
- // Server socket doesn't need session tickets. |
- rv = SSL_OptionSet(nss_fd_, SSL_ENABLE_SESSION_TICKETS, PR_FALSE); |
- if (rv != SECSuccess) { |
- LogFailedNSSFunction( |
- net_log_, "SSL_OptionSet", "SSL_ENABLE_SESSION_TICKETS"); |
- } |
- |
- // Doing this will force PR_Accept perform handshake as server. |
- rv = SSL_OptionSet(nss_fd_, SSL_HANDSHAKE_AS_CLIENT, PR_FALSE); |
- if (rv != SECSuccess) { |
- LogFailedNSSFunction(net_log_, "SSL_OptionSet", "SSL_HANDSHAKE_AS_CLIENT"); |
- return ERR_UNEXPECTED; |
- } |
- |
- rv = SSL_OptionSet(nss_fd_, SSL_HANDSHAKE_AS_SERVER, PR_TRUE); |
- if (rv != SECSuccess) { |
- LogFailedNSSFunction(net_log_, "SSL_OptionSet", "SSL_HANDSHAKE_AS_SERVER"); |
- return ERR_UNEXPECTED; |
- } |
- |
- rv = SSL_OptionSet(nss_fd_, SSL_REQUEST_CERTIFICATE, PR_FALSE); |
- if (rv != SECSuccess) { |
- LogFailedNSSFunction(net_log_, "SSL_OptionSet", "SSL_REQUEST_CERTIFICATE"); |
- return ERR_UNEXPECTED; |
- } |
- |
- rv = SSL_OptionSet(nss_fd_, SSL_REQUIRE_CERTIFICATE, PR_FALSE); |
- if (rv != SECSuccess) { |
- LogFailedNSSFunction(net_log_, "SSL_OptionSet", "SSL_REQUIRE_CERTIFICATE"); |
- return ERR_UNEXPECTED; |
- } |
- |
- rv = SSL_AuthCertificateHook(nss_fd_, OwnAuthCertHandler, this); |
- if (rv != SECSuccess) { |
- LogFailedNSSFunction(net_log_, "SSL_AuthCertificateHook", ""); |
- return ERR_UNEXPECTED; |
- } |
- |
- rv = SSL_HandshakeCallback(nss_fd_, HandshakeCallback, this); |
- if (rv != SECSuccess) { |
- LogFailedNSSFunction(net_log_, "SSL_HandshakeCallback", ""); |
- return ERR_UNEXPECTED; |
- } |
- |
- // Get a certificate of CERTCertificate structure. |
- std::string der_string; |
- if (!X509Certificate::GetDEREncoded(cert_->os_cert_handle(), &der_string)) |
- return ERR_UNEXPECTED; |
- |
- SECItem der_cert; |
- der_cert.data = reinterpret_cast<unsigned char*>(const_cast<char*>( |
- der_string.data())); |
- der_cert.len = der_string.length(); |
- der_cert.type = siDERCertBuffer; |
- |
- // Parse into a CERTCertificate structure. |
- CERTCertificate* cert = CERT_NewTempCertificate( |
- CERT_GetDefaultCertDB(), &der_cert, NULL, PR_FALSE, PR_TRUE); |
- if (!cert) { |
- LogFailedNSSFunction(net_log_, "CERT_NewTempCertificate", ""); |
- return MapNSSError(PORT_GetError()); |
- } |
- |
- // Get a key of SECKEYPrivateKey* structure. |
- std::vector<uint8_t> key_vector; |
- if (!key_->ExportPrivateKey(&key_vector)) { |
- CERT_DestroyCertificate(cert); |
- return ERR_UNEXPECTED; |
- } |
- |
- SECKEYPrivateKeyStr* private_key = NULL; |
- PK11SlotInfo* slot = PK11_GetInternalSlot(); |
- if (!slot) { |
- CERT_DestroyCertificate(cert); |
- return ERR_UNEXPECTED; |
- } |
- |
- SECItem der_private_key_info; |
- der_private_key_info.data = |
- const_cast<unsigned char*>(&key_vector.front()); |
- der_private_key_info.len = key_vector.size(); |
- // The server's RSA private key must be imported into NSS with the |
- // following key usage bits: |
- // - KU_KEY_ENCIPHERMENT, required for the RSA key exchange algorithm. |
- // - KU_DIGITAL_SIGNATURE, required for the DHE_RSA and ECDHE_RSA key |
- // exchange algorithms. |
- const unsigned int key_usage = KU_KEY_ENCIPHERMENT | KU_DIGITAL_SIGNATURE; |
- rv = PK11_ImportDERPrivateKeyInfoAndReturnKey( |
- slot, &der_private_key_info, NULL, NULL, PR_FALSE, PR_FALSE, |
- key_usage, &private_key, NULL); |
- PK11_FreeSlot(slot); |
- if (rv != SECSuccess) { |
- CERT_DestroyCertificate(cert); |
- return ERR_UNEXPECTED; |
- } |
- |
- // Assign server certificate and private key. |
- SSLKEAType cert_kea = NSS_FindCertKEAType(cert); |
- rv = SSL_ConfigSecureServer(nss_fd_, cert, private_key, cert_kea); |
- CERT_DestroyCertificate(cert); |
- SECKEY_DestroyPrivateKey(private_key); |
- |
- if (rv != SECSuccess) { |
- PRErrorCode prerr = PR_GetError(); |
- LOG(ERROR) << "Failed to config SSL server: " << prerr; |
- LogFailedNSSFunction(net_log_, "SSL_ConfigureSecureServer", ""); |
- return ERR_UNEXPECTED; |
- } |
- |
- // Tell SSL we're a server; needed if not letting NSPR do socket I/O |
- rv = SSL_ResetHandshake(nss_fd_, PR_TRUE); |
- if (rv != SECSuccess) { |
- LogFailedNSSFunction(net_log_, "SSL_ResetHandshake", ""); |
- return ERR_UNEXPECTED; |
- } |
- |
- return OK; |
-} |
- |
-void SSLServerSocketNSS::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 SSLClientSocketNSS::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 SSLServerSocketNSS::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 SSLServerSocketNSS::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 SSLServerSocketNSS::BufferSend(void) { |
- if (transport_send_busy_) |
- return ERR_IO_PENDING; |
- |
- const char* buf1; |
- const char* buf2; |
- unsigned int len1, len2; |
- if (memio_GetWriteParams(nss_bufs_, &buf1, &len1, &buf2, &len2)) { |
- // The error code itself is ignored, so just return ERR_ABORTED. |
- return ERR_ABORTED; |
- } |
- const size_t len = len1 + len2; |
- |
- int rv = 0; |
- if (len) { |
- scoped_refptr<IOBuffer> send_buffer(new IOBuffer(len)); |
- memcpy(send_buffer->data(), buf1, len1); |
- memcpy(send_buffer->data() + len1, buf2, len2); |
- rv = transport_socket_->Write( |
- send_buffer.get(), len, |
- base::Bind(&SSLServerSocketNSS::BufferSendComplete, |
- base::Unretained(this))); |
- if (rv == ERR_IO_PENDING) { |
- transport_send_busy_ = true; |
- } else { |
- memio_PutWriteResult(nss_bufs_, MapErrorToNSS(rv)); |
- } |
- } |
- |
- return rv; |
-} |
- |
-void SSLServerSocketNSS::BufferSendComplete(int result) { |
- memio_PutWriteResult(nss_bufs_, MapErrorToNSS(result)); |
- transport_send_busy_ = false; |
- OnSendComplete(result); |
-} |
- |
-int SSLServerSocketNSS::BufferRecv(void) { |
- if (transport_recv_busy_) return ERR_IO_PENDING; |
- |
- char* buf; |
- int nb = memio_GetReadParams(nss_bufs_, &buf); |
- int rv; |
- if (!nb) { |
- // buffer too full to read into, so no I/O possible at moment |
- rv = ERR_IO_PENDING; |
- } else { |
- recv_buffer_ = new IOBuffer(nb); |
- rv = transport_socket_->Read( |
- recv_buffer_.get(), nb, |
- base::Bind(&SSLServerSocketNSS::BufferRecvComplete, |
- base::Unretained(this))); |
- if (rv == ERR_IO_PENDING) { |
- transport_recv_busy_ = true; |
- } else { |
- if (rv > 0) |
- memcpy(buf, recv_buffer_->data(), rv); |
- memio_PutReadResult(nss_bufs_, MapErrorToNSS(rv)); |
- recv_buffer_ = NULL; |
- } |
- } |
- return rv; |
-} |
- |
-void SSLServerSocketNSS::BufferRecvComplete(int result) { |
- if (result > 0) { |
- char* buf; |
- memio_GetReadParams(nss_bufs_, &buf); |
- memcpy(buf, recv_buffer_->data(), result); |
- } |
- recv_buffer_ = NULL; |
- memio_PutReadResult(nss_bufs_, MapErrorToNSS(result)); |
- transport_recv_busy_ = false; |
- OnRecvComplete(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 SSLServerSocketNSS::DoTransportIO() { |
- bool network_moved = false; |
- if (nss_bufs_ != NULL) { |
- int rv; |
- // Read and write as much data as we can. The loop is neccessary |
- // because Write() may return synchronously. |
- do { |
- rv = BufferSend(); |
- if (rv > 0) |
- network_moved = true; |
- } while (rv > 0); |
- if (BufferRecv() >= 0) |
- network_moved = true; |
- } |
- return network_moved; |
-} |
- |
-int SSLServerSocketNSS::DoPayloadRead() { |
- DCHECK(user_read_buf_); |
- DCHECK_GT(user_read_buf_len_, 0); |
- int rv = PR_Read(nss_fd_, user_read_buf_->data(), user_read_buf_len_); |
- if (rv >= 0) |
- return rv; |
- PRErrorCode prerr = PR_GetError(); |
- if (prerr == PR_WOULD_BLOCK_ERROR) { |
- return ERR_IO_PENDING; |
- } |
- rv = MapNSSError(prerr); |
- net_log_.AddEvent(NetLog::TYPE_SSL_READ_ERROR, |
- CreateNetLogSSLErrorCallback(rv, prerr)); |
- return rv; |
-} |
- |
-int SSLServerSocketNSS::DoPayloadWrite() { |
- DCHECK(user_write_buf_); |
- int rv = PR_Write(nss_fd_, user_write_buf_->data(), user_write_buf_len_); |
- if (rv >= 0) |
- return rv; |
- PRErrorCode prerr = PR_GetError(); |
- if (prerr == PR_WOULD_BLOCK_ERROR) { |
- return ERR_IO_PENDING; |
- } |
- rv = MapNSSError(prerr); |
- net_log_.AddEvent(NetLog::TYPE_SSL_WRITE_ERROR, |
- CreateNetLogSSLErrorCallback(rv, prerr)); |
- return rv; |
-} |
- |
-int SSLServerSocketNSS::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 SSLServerSocketNSS::DoReadLoop(int result) { |
- DCHECK(completed_handshake_); |
- DCHECK(next_handshake_state_ == STATE_NONE); |
- |
- if (result < 0) |
- return result; |
- |
- if (!nss_bufs_) { |
- LOG(DFATAL) << "!nss_bufs_"; |
- int rv = ERR_UNEXPECTED; |
- net_log_.AddEvent(NetLog::TYPE_SSL_READ_ERROR, |
- CreateNetLogSSLErrorCallback(rv, 0)); |
- return rv; |
- } |
- |
- bool network_moved; |
- int rv; |
- do { |
- rv = DoPayloadRead(); |
- network_moved = DoTransportIO(); |
- } while (rv == ERR_IO_PENDING && network_moved); |
- return rv; |
-} |
- |
-int SSLServerSocketNSS::DoWriteLoop(int result) { |
- DCHECK(completed_handshake_); |
- DCHECK_EQ(next_handshake_state_, STATE_NONE); |
- |
- if (result < 0) |
- return result; |
- |
- if (!nss_bufs_) { |
- LOG(DFATAL) << "!nss_bufs_"; |
- int rv = ERR_UNEXPECTED; |
- net_log_.AddEvent(NetLog::TYPE_SSL_WRITE_ERROR, |
- CreateNetLogSSLErrorCallback(rv, 0)); |
- return rv; |
- } |
- |
- bool network_moved; |
- int rv; |
- do { |
- rv = DoPayloadWrite(); |
- network_moved = DoTransportIO(); |
- } while (rv == ERR_IO_PENDING && network_moved); |
- return rv; |
-} |
- |
-int SSLServerSocketNSS::DoHandshake() { |
- int net_error = OK; |
- SECStatus rv = SSL_ForceHandshake(nss_fd_); |
- |
- if (rv == SECSuccess) { |
- completed_handshake_ = true; |
- } else { |
- PRErrorCode prerr = PR_GetError(); |
- net_error = MapNSSError(prerr); |
- |
- // If not done, stay in this state |
- if (net_error == ERR_IO_PENDING) { |
- GotoState(STATE_HANDSHAKE); |
- } else { |
- LOG(ERROR) << "handshake failed; NSS error code " << prerr |
- << ", net_error " << net_error; |
- net_log_.AddEvent(NetLog::TYPE_SSL_HANDSHAKE_ERROR, |
- CreateNetLogSSLErrorCallback(net_error, prerr)); |
- } |
- } |
- return net_error; |
-} |
- |
-void SSLServerSocketNSS::DoHandshakeCallback(int rv) { |
- DCHECK_NE(rv, ERR_IO_PENDING); |
- base::ResetAndReturn(&user_handshake_callback_).Run(rv > OK ? OK : rv); |
-} |
- |
-void SSLServerSocketNSS::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 SSLServerSocketNSS::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); |
-} |
- |
-// static |
-// NSS calls this if an incoming certificate needs to be verified. |
-// Do nothing but return SECSuccess. |
-// This is called only in full handshake mode. |
-// Peer certificate is retrieved in HandshakeCallback() later, which is called |
-// in full handshake mode or in resumption handshake mode. |
-SECStatus SSLServerSocketNSS::OwnAuthCertHandler(void* arg, |
- PRFileDesc* socket, |
- PRBool checksig, |
- PRBool is_server) { |
- // TODO(hclam): Implement. |
- // Tell NSS to not verify the certificate. |
- return SECSuccess; |
-} |
- |
-// static |
-// NSS calls this when handshake is completed. |
-// After the SSL handshake is finished we need to verify the certificate. |
-void SSLServerSocketNSS::HandshakeCallback(PRFileDesc* socket, void* arg) { |
- // TODO(hclam): Implement. |
-} |
- |
-int SSLServerSocketNSS::Init() { |
- // Initialize the NSS SSL library in a threadsafe way. This also |
- // initializes the NSS base library. |
- EnsureNSSSSLInit(); |
- if (!NSS_IsInitialized()) |
- return ERR_UNEXPECTED; |
- |
- EnableSSLServerSockets(); |
- return OK; |
-} |
- |
-} // namespace |
- |
-std::unique_ptr<SSLServerContext> CreateSSLServerContext( |
- X509Certificate* certificate, |
- const crypto::RSAPrivateKey& key, |
- const SSLServerConfig& ssl_server_config) { |
- return std::unique_ptr<SSLServerContext>( |
- new SSLServerContextNSS(certificate, key, ssl_server_config)); |
-} |
- |
-SSLServerContextNSS::SSLServerContextNSS( |
- X509Certificate* certificate, |
- const crypto::RSAPrivateKey& key, |
- const SSLServerConfig& ssl_server_config) |
- : ssl_server_config_(ssl_server_config), |
- cert_(certificate), |
- key_(key.Copy()) { |
- CHECK(key_); |
-} |
- |
-SSLServerContextNSS::~SSLServerContextNSS() {} |
- |
-std::unique_ptr<SSLServerSocket> SSLServerContextNSS::CreateSSLServerSocket( |
- std::unique_ptr<StreamSocket> socket) { |
- DCHECK(g_nss_server_sockets_init) << "EnableSSLServerSockets() has not been" |
- << " called yet!"; |
- |
- return std::unique_ptr<SSLServerSocket>(new SSLServerSocketNSS( |
- std::move(socket), cert_.get(), *key_, ssl_server_config_)); |
-} |
- |
-void EnableSSLServerSockets() { |
- g_nss_ssl_server_init_singleton.Get(); |
-} |
- |
-} // namespace net |