summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorhclam@chromium.org <hclam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-12-23 09:54:15 +0000
committerhclam@chromium.org <hclam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-12-23 09:54:15 +0000
commitf61c397ae7c8d07762b02d6578928163e2a8eca0 (patch)
tree3c029791e1a36b1218b5378c5e7b579d84023755
parent88616f47602e8a2a16c65ca0a59444e0ce550772 (diff)
downloadchromium_src-f61c397ae7c8d07762b02d6578928163e2a8eca0.zip
chromium_src-f61c397ae7c8d07762b02d6578928163e2a8eca0.tar.gz
chromium_src-f61c397ae7c8d07762b02d6578928163e2a8eca0.tar.bz2
Defines SSLServerSocket and implements SSLServerSocketNSS
Defines a SSLServerSocket interface. Implement this interface using NSS as SSLServerSocketNSS. This is the first version of the code. It disables several functions of NSS like caching, session ticket, reneogotiation, etc. This is implemented to suit the needs of Chromoting. Additional features of this socket will be added when necessary. BUG=None TEST=None Review URL: http://codereview.chromium.org/5746003 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@70041 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--base/crypto/rsa_private_key.h1
-rw-r--r--base/nss_util.cc4
-rw-r--r--base/nss_util.h4
-rw-r--r--net/base/net_log_event_type_list.h3
-rw-r--r--net/base/ssl_config_service.cc5
-rw-r--r--net/base/ssl_config_service.h5
-rw-r--r--net/base/x509_certificate.h5
-rw-r--r--net/base/x509_certificate_mac.cc11
-rw-r--r--net/base/x509_certificate_nss.cc9
-rw-r--r--net/base/x509_certificate_openssl.cc5
-rw-r--r--net/base/x509_certificate_unittest.cc12
-rw-r--r--net/base/x509_certificate_win.cc9
-rw-r--r--net/net.gyp8
-rw-r--r--net/socket/nss_ssl_util.cc240
-rw-r--r--net/socket/nss_ssl_util.h36
-rw-r--r--net/socket/ssl_client_socket_nss.cc232
-rw-r--r--net/socket/ssl_server_socket.h53
-rw-r--r--net/socket/ssl_server_socket_nss.cc677
-rw-r--r--net/socket/ssl_server_socket_nss.h133
-rw-r--r--net/socket/ssl_server_socket_unittest.cc369
20 files changed, 1598 insertions, 223 deletions
diff --git a/base/crypto/rsa_private_key.h b/base/crypto/rsa_private_key.h
index 879c1f4..bac4250 100644
--- a/base/crypto/rsa_private_key.h
+++ b/base/crypto/rsa_private_key.h
@@ -165,6 +165,7 @@ class PrivateKeyInfoCodec {
// Encapsulates an RSA private key. Can be used to generate new keys, export
// keys to other formats, or to extract a public key.
+// TODO(hclam): This class should be ref-counted so it can be reused easily.
class RSAPrivateKey {
public:
// Create a new random instance. Can return NULL if initialization fails.
diff --git a/base/nss_util.cc b/base/nss_util.cc
index 580fb60..36394da 100644
--- a/base/nss_util.cc
+++ b/base/nss_util.cc
@@ -333,6 +333,10 @@ void EnsureNSSInit() {
g_nss_singleton.Get();
}
+bool CheckNSSVersion(const char* version) {
+ return !!NSS_VersionCheck(version);
+}
+
#if defined(USE_NSS)
bool OpenTestNSSDB(const FilePath& path, const char* description) {
return g_nss_singleton.Get().OpenTestNSSDB(path, description);
diff --git a/base/nss_util.h b/base/nss_util.h
index 15b624c..d1e36ac 100644
--- a/base/nss_util.h
+++ b/base/nss_util.h
@@ -30,6 +30,10 @@ void EnsureNSPRInit();
// ever be initialized once. NSS will be properly shut down on program exit.
void EnsureNSSInit();
+// Check if the current NSS version is greater than or equals to |version|.
+// A sample version string is "3.12.3".
+bool CheckNSSVersion(const char* version);
+
#if defined(OS_CHROMEOS)
// Open the r/w nssdb that's stored inside the user's encrypted home directory.
void OpenPersistentNSSDB();
diff --git a/net/base/net_log_event_type_list.h b/net/base/net_log_event_type_list.h
index e571685..f1bc4f8 100644
--- a/net/base/net_log_event_type_list.h
+++ b/net/base/net_log_event_type_list.h
@@ -325,6 +325,9 @@ EVENT_TYPE(SOCKS_UNKNOWN_ADDRESS_TYPE)
// The start/end of a SSL connect().
EVENT_TYPE(SSL_CONNECT)
+// The start/end of a SSL accept().
+EVENT_TYPE(SSL_ACCEPT)
+
// An SSL error occurred while trying to do the indicated activity.
// The following parameters are attached to the event:
// {
diff --git a/net/base/ssl_config_service.cc b/net/base/ssl_config_service.cc
index 9b0a903..d02df38 100644
--- a/net/base/ssl_config_service.cc
+++ b/net/base/ssl_config_service.cc
@@ -23,8 +23,9 @@ SSLConfig::SSLConfig()
: rev_checking_enabled(true), ssl3_enabled(true),
tls1_enabled(true), dnssec_enabled(false), snap_start_enabled(false),
dns_cert_provenance_checking_enabled(false),
- mitm_proxies_allowed(false), false_start_enabled(true),
- send_client_cert(false), verify_ev_cert(false), ssl3_fallback(false) {
+ session_resume_disabled(false), mitm_proxies_allowed(false),
+ false_start_enabled(true), send_client_cert(false),
+ verify_ev_cert(false), ssl3_fallback(false) {
}
SSLConfig::~SSLConfig() {
diff --git a/net/base/ssl_config_service.h b/net/base/ssl_config_service.h
index c1ae553..de2ebef 100644
--- a/net/base/ssl_config_service.h
+++ b/net/base/ssl_config_service.h
@@ -32,6 +32,11 @@ struct SSLConfig {
// True if we'll do async checks for certificate provenance using DNS.
bool dns_cert_provenance_checking_enabled;
+ // TODO(hclam): This option is used to simplify the SSLServerSocketNSS
+ // implementation and should be removed when session caching is implemented.
+ // See http://crbug.com/67236 for more details.
+ bool session_resume_disabled; // Don't allow session resume.
+
// Cipher suites which should be explicitly prevented from being used in
// addition to those disabled by the net built-in policy -- by default, all
// cipher suites supported by the underlying SSL implementation will be
diff --git a/net/base/x509_certificate.h b/net/base/x509_certificate.h
index 3ee7304..c59c33c 100644
--- a/net/base/x509_certificate.h
+++ b/net/base/x509_certificate.h
@@ -287,6 +287,11 @@ class X509Certificate : public base::RefCountedThreadSafe<X509Certificate> {
int flags,
CertVerifyResult* verify_result) const;
+ // This method returns the DER encoded certificate.
+ // If the return value is true then the DER encoded certificate is available.
+ // The content of the DER encoded certificate is written to |encoded|.
+ bool GetDEREncoded(std::string* encoded);
+
OSCertHandle os_cert_handle() const { return cert_handle_; }
// Returns true if two OSCertHandles refer to identical certificates.
diff --git a/net/base/x509_certificate_mac.cc b/net/base/x509_certificate_mac.cc
index f7c89e4..fd965cb3 100644
--- a/net/base/x509_certificate_mac.cc
+++ b/net/base/x509_certificate_mac.cc
@@ -651,6 +651,17 @@ int X509Certificate::Verify(const std::string& hostname, int flags,
return OK;
}
+bool X509Certificate::GetDEREncoded(std::string* encoded) {
+ encoded->clear();
+ CSSM_DATA der_data;
+ if(SecCertificateGetData(cert_handle_, &der_data) == noErr) {
+ encoded->append(reinterpret_cast<char*>(der_data.Data),
+ der_data.Length);
+ return true;
+ }
+ return false;
+}
+
bool X509Certificate::VerifyEV() const {
// We don't call this private method, but we do need to implement it because
// it's defined in x509_certificate.h. We perform EV checking in the
diff --git a/net/base/x509_certificate_nss.cc b/net/base/x509_certificate_nss.cc
index 2962cb5..05e736c 100644
--- a/net/base/x509_certificate_nss.cc
+++ b/net/base/x509_certificate_nss.cc
@@ -829,6 +829,15 @@ bool X509Certificate::VerifyEV() const {
return true;
}
+bool X509Certificate::GetDEREncoded(std::string* encoded) {
+ if (!cert_handle_->derCert.len)
+ return false;
+ encoded->clear();
+ encoded->append(reinterpret_cast<char*>(cert_handle_->derCert.data),
+ cert_handle_->derCert.len);
+ return true;
+}
+
// static
bool X509Certificate::IsSameOSCert(X509Certificate::OSCertHandle a,
X509Certificate::OSCertHandle b) {
diff --git a/net/base/x509_certificate_openssl.cc b/net/base/x509_certificate_openssl.cc
index c6ffb2c..cf43610 100644
--- a/net/base/x509_certificate_openssl.cc
+++ b/net/base/x509_certificate_openssl.cc
@@ -462,6 +462,11 @@ int X509Certificate::Verify(const std::string& hostname,
return OK;
}
+bool X509Certificate::GetDEREncoded(std::string* encoded) {
+ // TODO(port): Implement.
+ return false;
+}
+
// static
bool X509Certificate::IsSameOSCert(X509Certificate::OSCertHandle a,
X509Certificate::OSCertHandle b) {
diff --git a/net/base/x509_certificate_unittest.cc b/net/base/x509_certificate_unittest.cc
index 83c11fa..dba5ef3 100644
--- a/net/base/x509_certificate_unittest.cc
+++ b/net/base/x509_certificate_unittest.cc
@@ -672,6 +672,18 @@ TEST(X509CertificateTest, CreateSelfSigned) {
EXPECT_EQ("subject", cert->subject().GetDisplayName());
EXPECT_FALSE(cert->HasExpired());
}
+
+TEST(X509CertificateTest, GetDEREncoded) {
+ scoped_ptr<base::RSAPrivateKey> private_key(
+ base::RSAPrivateKey::Create(1024));
+ scoped_refptr<net::X509Certificate> cert =
+ net::X509Certificate::CreateSelfSigned(
+ private_key.get(), "CN=subject", 0, base::TimeDelta::FromDays(1));
+
+ std::string der_cert;
+ EXPECT_TRUE(cert->GetDEREncoded(&der_cert));
+ EXPECT_FALSE(der_cert.empty());
+}
#endif
class X509CertificateParseTest
diff --git a/net/base/x509_certificate_win.cc b/net/base/x509_certificate_win.cc
index 568c1fd..663563d 100644
--- a/net/base/x509_certificate_win.cc
+++ b/net/base/x509_certificate_win.cc
@@ -843,6 +843,15 @@ int X509Certificate::Verify(const std::string& hostname,
return OK;
}
+bool X509Certificate::GetDEREncoded(std::string* encoded) {
+ if (!cert_handle_->pbCertEncoded || !cert_handle_->cbCertEncoded)
+ return false;
+ encoded->clear();
+ encoded->append(reinterpret_cast<char*>(cert_handle_->pbCertEncoded),
+ cert_handle_->cbCertEncoded);
+ return true;
+}
+
// Returns true if the certificate is an extended-validation certificate.
//
// This function checks the certificatePolicies extensions of the
diff --git a/net/net.gyp b/net/net.gyp
index 5e58283..2c24152 100644
--- a/net/net.gyp
+++ b/net/net.gyp
@@ -594,6 +594,8 @@
'socket/client_socket_pool_manager.h',
'socket/dns_cert_provenance_checker.cc',
'socket/dns_cert_provenance_checker.h',
+ 'socket/nss_ssl_util.cc',
+ 'socket/nss_ssl_util.h',
'socket/socket.h',
'socket/socks5_client_socket.cc',
'socket/socks5_client_socket.h',
@@ -619,6 +621,9 @@
'socket/ssl_client_socket_win.h',
'socket/ssl_error_params.cc',
'socket/ssl_error_params.h',
+ 'socket/ssl_server_socket.h',
+ 'socket/ssl_server_socket_nss.cc',
+ 'socket/ssl_server_socket_nss.h',
'socket/ssl_host_info.cc',
'socket/ssl_host_info.h',
'socket/tcp_client_socket.cc',
@@ -750,6 +755,8 @@
'socket/ssl_client_socket_nss.h',
'socket/ssl_client_socket_nss_factory.cc',
'socket/ssl_client_socket_nss_factory.h',
+ 'socket/ssl_server_socket_nss.cc',
+ 'socket/ssl_server_socket_nss.h',
],
},
{ # else !use_openssl: remove the unneeded files
@@ -960,6 +967,7 @@
'socket/socks_client_socket_unittest.cc',
'socket/ssl_client_socket_unittest.cc',
'socket/ssl_client_socket_pool_unittest.cc',
+ 'socket/ssl_server_socket_unittest.cc',
'socket/tcp_client_socket_pool_unittest.cc',
'socket/tcp_client_socket_unittest.cc',
'socket_stream/socket_stream_metrics_unittest.cc',
diff --git a/net/socket/nss_ssl_util.cc b/net/socket/nss_ssl_util.cc
new file mode 100644
index 0000000..eb8bafb
--- /dev/null
+++ b/net/socket/nss_ssl_util.cc
@@ -0,0 +1,240 @@
+// Copyright (c) 2010 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/nss_ssl_util.h"
+
+#include <nss.h>
+#include <secerr.h>
+#include <ssl.h>
+#include <sslerr.h>
+
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/nss_util.h"
+#include "base/singleton.h"
+#include "base/thread_restrictions.h"
+#include "base/values.h"
+#include "net/base/net_errors.h"
+#include "net/base/net_log.h"
+
+namespace net {
+
+class NSSSSLInitSingleton {
+ public:
+ NSSSSLInitSingleton() {
+ base::EnsureNSSInit();
+
+ NSS_SetDomesticPolicy();
+
+#if defined(USE_SYSTEM_SSL)
+ // Use late binding to avoid scary but benign warning
+ // "Symbol `SSL_ImplementedCiphers' has different size in shared object,
+ // consider re-linking"
+ // TODO(wtc): Use the new SSL_GetImplementedCiphers and
+ // SSL_GetNumImplementedCiphers functions when we require NSS 3.12.6.
+ // See https://bugzilla.mozilla.org/show_bug.cgi?id=496993.
+ const PRUint16* pSSL_ImplementedCiphers = static_cast<const PRUint16*>(
+ dlsym(RTLD_DEFAULT, "SSL_ImplementedCiphers"));
+ if (pSSL_ImplementedCiphers == NULL) {
+ NOTREACHED() << "Can't get list of supported ciphers";
+ return;
+ }
+#else
+#define pSSL_ImplementedCiphers SSL_ImplementedCiphers
+#endif
+
+ // Explicitly enable exactly those ciphers with keys of at least 80 bits
+ for (int i = 0; i < SSL_NumImplementedCiphers; i++) {
+ SSLCipherSuiteInfo info;
+ if (SSL_GetCipherSuiteInfo(pSSL_ImplementedCiphers[i], &info,
+ sizeof(info)) == SECSuccess) {
+ SSL_CipherPrefSetDefault(pSSL_ImplementedCiphers[i],
+ (info.effectiveKeyBits >= 80));
+ }
+ }
+
+ // Enable SSL.
+ SSL_OptionSetDefault(SSL_SECURITY, PR_TRUE);
+
+ // All other SSL options are set per-session by SSLClientSocket and
+ // SSLServerSocket.
+ }
+
+ ~NSSSSLInitSingleton() {
+ // Have to clear the cache, or NSS_Shutdown fails with SEC_ERROR_BUSY.
+ SSL_ClearSessionCache();
+ }
+};
+
+static base::LazyInstance<NSSSSLInitSingleton> g_nss_ssl_init_singleton(
+ base::LINKER_INITIALIZED);
+
+// Initialize the NSS SSL library if it isn't already initialized. This must
+// be called before any other NSS SSL functions. This function is
+// thread-safe, and the NSS SSL library will only ever be initialized once.
+// The NSS SSL library will be properly shut down on program exit.
+void EnsureNSSSSLInit() {
+ // Initializing SSL causes us to do blocking IO.
+ // Temporarily allow it until we fix
+ // http://code.google.com/p/chromium/issues/detail?id=59847
+ base::ThreadRestrictions::ScopedAllowIO allow_io;
+
+ g_nss_ssl_init_singleton.Get();
+}
+
+// Map a Chromium net error code to an NSS error code.
+// See _MD_unix_map_default_error in the NSS source
+// tree for inspiration.
+PRErrorCode MapErrorToNSS(int result) {
+ if (result >=0)
+ return result;
+
+ switch (result) {
+ case ERR_IO_PENDING:
+ return PR_WOULD_BLOCK_ERROR;
+ case ERR_ACCESS_DENIED:
+ case ERR_NETWORK_ACCESS_DENIED:
+ // For connect, this could be mapped to PR_ADDRESS_NOT_SUPPORTED_ERROR.
+ return PR_NO_ACCESS_RIGHTS_ERROR;
+ case ERR_NOT_IMPLEMENTED:
+ return PR_NOT_IMPLEMENTED_ERROR;
+ case ERR_INTERNET_DISCONNECTED: // Equivalent to ENETDOWN.
+ return PR_NETWORK_UNREACHABLE_ERROR; // Best approximation.
+ case ERR_CONNECTION_TIMED_OUT:
+ case ERR_TIMED_OUT:
+ return PR_IO_TIMEOUT_ERROR;
+ case ERR_CONNECTION_RESET:
+ return PR_CONNECT_RESET_ERROR;
+ case ERR_CONNECTION_ABORTED:
+ return PR_CONNECT_ABORTED_ERROR;
+ case ERR_CONNECTION_REFUSED:
+ return PR_CONNECT_REFUSED_ERROR;
+ case ERR_ADDRESS_UNREACHABLE:
+ return PR_HOST_UNREACHABLE_ERROR; // Also PR_NETWORK_UNREACHABLE_ERROR.
+ case ERR_ADDRESS_INVALID:
+ return PR_ADDRESS_NOT_AVAILABLE_ERROR;
+ case ERR_NAME_NOT_RESOLVED:
+ return PR_DIRECTORY_LOOKUP_ERROR;
+ default:
+ LOG(WARNING) << "MapErrorToNSS " << result
+ << " mapped to PR_UNKNOWN_ERROR";
+ return PR_UNKNOWN_ERROR;
+ }
+}
+
+// The default error mapping function.
+// Maps an NSS error code to a network error code.
+int MapNSSError(PRErrorCode err) {
+ // TODO(port): fill this out as we learn what's important
+ switch (err) {
+ case PR_WOULD_BLOCK_ERROR:
+ return ERR_IO_PENDING;
+ case PR_ADDRESS_NOT_SUPPORTED_ERROR: // For connect.
+ case PR_NO_ACCESS_RIGHTS_ERROR:
+ return ERR_ACCESS_DENIED;
+ case PR_IO_TIMEOUT_ERROR:
+ return ERR_TIMED_OUT;
+ case PR_CONNECT_RESET_ERROR:
+ return ERR_CONNECTION_RESET;
+ case PR_CONNECT_ABORTED_ERROR:
+ return ERR_CONNECTION_ABORTED;
+ case PR_CONNECT_REFUSED_ERROR:
+ return ERR_CONNECTION_REFUSED;
+ case PR_HOST_UNREACHABLE_ERROR:
+ case PR_NETWORK_UNREACHABLE_ERROR:
+ return ERR_ADDRESS_UNREACHABLE;
+ case PR_ADDRESS_NOT_AVAILABLE_ERROR:
+ return ERR_ADDRESS_INVALID;
+ case PR_INVALID_ARGUMENT_ERROR:
+ return ERR_INVALID_ARGUMENT;
+ case PR_END_OF_FILE_ERROR:
+ return ERR_CONNECTION_CLOSED;
+ case PR_NOT_IMPLEMENTED_ERROR:
+ return ERR_NOT_IMPLEMENTED;
+
+ case SEC_ERROR_INVALID_ARGS:
+ return ERR_INVALID_ARGUMENT;
+
+ case SSL_ERROR_SSL_DISABLED:
+ return ERR_NO_SSL_VERSIONS_ENABLED;
+ case SSL_ERROR_NO_CYPHER_OVERLAP:
+ case SSL_ERROR_UNSUPPORTED_VERSION:
+ return ERR_SSL_VERSION_OR_CIPHER_MISMATCH;
+ case SSL_ERROR_HANDSHAKE_FAILURE_ALERT:
+ case SSL_ERROR_HANDSHAKE_UNEXPECTED_ALERT:
+ case SSL_ERROR_ILLEGAL_PARAMETER_ALERT:
+ return ERR_SSL_PROTOCOL_ERROR;
+ case SSL_ERROR_DECOMPRESSION_FAILURE_ALERT:
+ return ERR_SSL_DECOMPRESSION_FAILURE_ALERT;
+ case SSL_ERROR_BAD_MAC_ALERT:
+ return ERR_SSL_BAD_RECORD_MAC_ALERT;
+ case SSL_ERROR_UNSAFE_NEGOTIATION:
+ return ERR_SSL_UNSAFE_NEGOTIATION;
+ case SSL_ERROR_WEAK_SERVER_KEY:
+ return ERR_SSL_WEAK_SERVER_EPHEMERAL_DH_KEY;
+
+ default: {
+ if (IS_SSL_ERROR(err)) {
+ LOG(WARNING) << "Unknown SSL error " << err <<
+ " mapped to net::ERR_SSL_PROTOCOL_ERROR";
+ return ERR_SSL_PROTOCOL_ERROR;
+ }
+ LOG(WARNING) << "Unknown error " << err <<
+ " mapped to net::ERR_FAILED";
+ return ERR_FAILED;
+ }
+ }
+}
+
+// Context-sensitive error mapping functions.
+int MapNSSHandshakeError(PRErrorCode err) {
+ switch (err) {
+ // If the server closed on us, it is a protocol error.
+ // Some TLS-intolerant servers do this when we request TLS.
+ case PR_END_OF_FILE_ERROR:
+ // The handshake may fail because some signature (for example, the
+ // signature in the ServerKeyExchange message for an ephemeral
+ // Diffie-Hellman cipher suite) is invalid.
+ case SEC_ERROR_BAD_SIGNATURE:
+ return ERR_SSL_PROTOCOL_ERROR;
+ default:
+ return MapNSSError(err);
+ }
+}
+
+// Extra parameters to attach to the NetLog when we receive an error in response
+// to a call to an NSS function. Used instead of SSLErrorParams with
+// events of type TYPE_SSL_NSS_ERROR. Automatically looks up last PR error.
+class SSLFailedNSSFunctionParams : public NetLog::EventParameters {
+ public:
+ // |param| is ignored if it has a length of 0.
+ SSLFailedNSSFunctionParams(const std::string& function,
+ const std::string& param)
+ : function_(function), param_(param), ssl_lib_error_(PR_GetError()) {
+ }
+
+ virtual Value* ToValue() const {
+ DictionaryValue* dict = new DictionaryValue();
+ dict->SetString("function", function_);
+ if (!param_.empty())
+ dict->SetString("param", param_);
+ dict->SetInteger("ssl_lib_error", ssl_lib_error_);
+ return dict;
+ }
+
+ private:
+ const std::string function_;
+ const std::string param_;
+ const PRErrorCode ssl_lib_error_;
+};
+
+void LogFailedNSSFunction(const BoundNetLog& net_log,
+ const char* function,
+ const char* param) {
+ net_log.AddEvent(
+ NetLog::TYPE_SSL_NSS_ERROR,
+ make_scoped_refptr(new SSLFailedNSSFunctionParams(function, param)));
+}
+
+} // namespace net
diff --git a/net/socket/nss_ssl_util.h b/net/socket/nss_ssl_util.h
new file mode 100644
index 0000000..64bf3cf
--- /dev/null
+++ b/net/socket/nss_ssl_util.h
@@ -0,0 +1,36 @@
+// Copyright (c) 2010 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.
+
+// This file is only inclued in ssl_client_socket_nss.cc and
+// ssl_server_socket_nss.cc to share common functions of NSS.
+
+#ifndef NET_SOCKET_NSS_SSL_UTIL_H_
+#define NEt_SOCKET_NSS_SSL_UTIL_H_
+
+#include <prerror.h>
+
+namespace net {
+
+class BoundNetLog;
+
+// Initalize NSS SSL library.
+void EnsureNSSSSLInit();
+
+// Log a failed NSS funcion call.
+void LogFailedNSSFunction(const BoundNetLog& net_log,
+ const char* function,
+ const char* param);
+
+// Map network error code to NSS error code.
+PRErrorCode MapErrorToNSS(int result);
+
+// Map NSS error code to network error code.
+int MapNSSError(PRErrorCode err);
+
+// Map NSS handshake error to network error code.
+int MapNSSHandshakeError(PRErrorCode err);
+
+} // namespace net
+
+#endif // NET_SOCKET_NSS_SSL_UTIL_H_
diff --git a/net/socket/ssl_client_socket_nss.cc b/net/socket/ssl_client_socket_nss.cc
index 3f56b59..49e065f 100644
--- a/net/socket/ssl_client_socket_nss.cc
+++ b/net/socket/ssl_client_socket_nss.cc
@@ -63,7 +63,6 @@
#include "base/compiler_specific.h"
#include "base/metrics/histogram.h"
-#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/nss_util.h"
#include "base/string_number_conversions.h"
@@ -88,6 +87,7 @@
#include "net/ocsp/nss_ocsp.h"
#include "net/socket/client_socket_handle.h"
#include "net/socket/dns_cert_provenance_checker.h"
+#include "net/socket/nss_ssl_util.h"
#include "net/socket/ssl_error_params.h"
#include "net/socket/ssl_host_info.h"
@@ -139,183 +139,6 @@ namespace net {
namespace {
-class NSSSSLInitSingleton {
- public:
- NSSSSLInitSingleton() {
- base::EnsureNSSInit();
-
- NSS_SetDomesticPolicy();
-
-#if defined(USE_SYSTEM_SSL)
- // Use late binding to avoid scary but benign warning
- // "Symbol `SSL_ImplementedCiphers' has different size in shared object,
- // consider re-linking"
- // TODO(wtc): Use the new SSL_GetImplementedCiphers and
- // SSL_GetNumImplementedCiphers functions when we require NSS 3.12.6.
- // See https://bugzilla.mozilla.org/show_bug.cgi?id=496993.
- const PRUint16* pSSL_ImplementedCiphers = static_cast<const PRUint16*>(
- dlsym(RTLD_DEFAULT, "SSL_ImplementedCiphers"));
- if (pSSL_ImplementedCiphers == NULL) {
- NOTREACHED() << "Can't get list of supported ciphers";
- return;
- }
-#else
-#define pSSL_ImplementedCiphers SSL_ImplementedCiphers
-#endif
-
- // Explicitly enable exactly those ciphers with keys of at least 80 bits
- for (int i = 0; i < SSL_NumImplementedCiphers; i++) {
- SSLCipherSuiteInfo info;
- if (SSL_GetCipherSuiteInfo(pSSL_ImplementedCiphers[i], &info,
- sizeof(info)) == SECSuccess) {
- SSL_CipherPrefSetDefault(pSSL_ImplementedCiphers[i],
- (info.effectiveKeyBits >= 80));
- }
- }
-
- // Enable SSL.
- SSL_OptionSetDefault(SSL_SECURITY, PR_TRUE);
-
- // All other SSL options are set per-session by SSLClientSocket.
- }
-
- ~NSSSSLInitSingleton() {
- // Have to clear the cache, or NSS_Shutdown fails with SEC_ERROR_BUSY.
- SSL_ClearSessionCache();
- }
-};
-
-static base::LazyInstance<NSSSSLInitSingleton> g_nss_ssl_init_singleton(
- base::LINKER_INITIALIZED);
-
-// Initialize the NSS SSL library if it isn't already initialized. This must
-// be called before any other NSS SSL functions. This function is
-// thread-safe, and the NSS SSL library will only ever be initialized once.
-// The NSS SSL library will be properly shut down on program exit.
-void EnsureNSSSSLInit() {
- // Initializing SSL causes us to do blocking IO.
- // Temporarily allow it until we fix
- // http://code.google.com/p/chromium/issues/detail?id=59847
- base::ThreadRestrictions::ScopedAllowIO allow_io;
-
- g_nss_ssl_init_singleton.Get();
-}
-
-// The default error mapping function.
-// Maps an NSPR error code to a network error code.
-int MapNSPRError(PRErrorCode err) {
- // TODO(port): fill this out as we learn what's important
- switch (err) {
- case PR_WOULD_BLOCK_ERROR:
- return ERR_IO_PENDING;
- case PR_ADDRESS_NOT_SUPPORTED_ERROR: // For connect.
- case PR_NO_ACCESS_RIGHTS_ERROR:
- return ERR_ACCESS_DENIED;
- case PR_IO_TIMEOUT_ERROR:
- return ERR_TIMED_OUT;
- case PR_CONNECT_RESET_ERROR:
- return ERR_CONNECTION_RESET;
- case PR_CONNECT_ABORTED_ERROR:
- return ERR_CONNECTION_ABORTED;
- case PR_CONNECT_REFUSED_ERROR:
- return ERR_CONNECTION_REFUSED;
- case PR_HOST_UNREACHABLE_ERROR:
- case PR_NETWORK_UNREACHABLE_ERROR:
- return ERR_ADDRESS_UNREACHABLE;
- case PR_ADDRESS_NOT_AVAILABLE_ERROR:
- return ERR_ADDRESS_INVALID;
- case PR_INVALID_ARGUMENT_ERROR:
- return ERR_INVALID_ARGUMENT;
- case PR_END_OF_FILE_ERROR:
- return ERR_CONNECTION_CLOSED;
- case PR_NOT_IMPLEMENTED_ERROR:
- return ERR_NOT_IMPLEMENTED;
-
- case SEC_ERROR_INVALID_ARGS:
- return ERR_INVALID_ARGUMENT;
-
- case SSL_ERROR_SSL_DISABLED:
- return ERR_NO_SSL_VERSIONS_ENABLED;
- case SSL_ERROR_NO_CYPHER_OVERLAP:
- case SSL_ERROR_UNSUPPORTED_VERSION:
- return ERR_SSL_VERSION_OR_CIPHER_MISMATCH;
- case SSL_ERROR_HANDSHAKE_FAILURE_ALERT:
- case SSL_ERROR_HANDSHAKE_UNEXPECTED_ALERT:
- case SSL_ERROR_ILLEGAL_PARAMETER_ALERT:
- return ERR_SSL_PROTOCOL_ERROR;
- case SSL_ERROR_DECOMPRESSION_FAILURE_ALERT:
- return ERR_SSL_DECOMPRESSION_FAILURE_ALERT;
- case SSL_ERROR_BAD_MAC_ALERT:
- return ERR_SSL_BAD_RECORD_MAC_ALERT;
- case SSL_ERROR_UNSAFE_NEGOTIATION:
- return ERR_SSL_UNSAFE_NEGOTIATION;
- case SSL_ERROR_WEAK_SERVER_KEY:
- return ERR_SSL_WEAK_SERVER_EPHEMERAL_DH_KEY;
-
- default: {
- if (IS_SSL_ERROR(err)) {
- LOG(WARNING) << "Unknown SSL error " << err <<
- " mapped to net::ERR_SSL_PROTOCOL_ERROR";
- return ERR_SSL_PROTOCOL_ERROR;
- }
- LOG(WARNING) << "Unknown error " << err <<
- " mapped to net::ERR_FAILED";
- return ERR_FAILED;
- }
- }
-}
-
-// Context-sensitive error mapping functions.
-
-int MapHandshakeError(PRErrorCode err) {
- switch (err) {
- // If the server closed on us, it is a protocol error.
- // Some TLS-intolerant servers do this when we request TLS.
- case PR_END_OF_FILE_ERROR:
- // The handshake may fail because some signature (for example, the
- // signature in the ServerKeyExchange message for an ephemeral
- // Diffie-Hellman cipher suite) is invalid.
- case SEC_ERROR_BAD_SIGNATURE:
- return ERR_SSL_PROTOCOL_ERROR;
- default:
- return MapNSPRError(err);
- }
-}
-
-// Extra parameters to attach to the NetLog when we receive an error in response
-// to a call to an NSS function. Used instead of SSLErrorParams with
-// events of type TYPE_SSL_NSS_ERROR. Automatically looks up last PR error.
-class SSLFailedNSSFunctionParams : public NetLog::EventParameters {
- public:
- // |param| is ignored if it has a length of 0.
- SSLFailedNSSFunctionParams(const std::string& function,
- const std::string& param)
- : function_(function), param_(param), ssl_lib_error_(PR_GetError()) {
- }
-
- virtual Value* ToValue() const {
- DictionaryValue* dict = new DictionaryValue();
- dict->SetString("function", function_);
- if (!param_.empty())
- dict->SetString("param", param_);
- dict->SetInteger("ssl_lib_error", ssl_lib_error_);
- return dict;
- }
-
- private:
- const std::string function_;
- const std::string param_;
- const PRErrorCode ssl_lib_error_;
-};
-
-void LogFailedNSSFunction(const BoundNetLog& net_log,
- const char* function,
- const char* param) {
- net_log.AddEvent(
- NetLog::TYPE_SSL_NSS_ERROR,
- make_scoped_refptr(new SSLFailedNSSFunctionParams(function, param)));
-}
-
#if defined(OS_WIN)
// This callback is intended to be used with CertFindChainInStore. In addition
@@ -736,6 +559,13 @@ int SSLClientSocketNSS::InitializeSSLOptions() {
#error "You need to install NSS-3.12 or later to build chromium"
#endif
+ rv = SSL_OptionSet(nss_fd_, SSL_NO_CACHE,
+ ssl_config_.session_resume_disabled);
+ if (rv != SECSuccess) {
+ LogFailedNSSFunction(net_log_, "SSL_OptionSet", "SSL_NO_CACHE");
+ return ERR_UNEXPECTED;
+ }
+
#ifdef SSL_ENABLE_DEFLATE
// Some web servers have been found to break if TLS is used *or* if DEFLATE
// is advertised. Thus, if TLS is disabled (probably because we are doing
@@ -1363,46 +1193,6 @@ void SSLClientSocketNSS::OnRecvComplete(int result) {
LeaveFunction("");
}
-// Map a Chromium net error code to an NSS error code.
-// See _MD_unix_map_default_error in the NSS source
-// tree for inspiration.
-static PRErrorCode MapErrorToNSS(int result) {
- if (result >=0)
- return result;
-
- switch (result) {
- case ERR_IO_PENDING:
- return PR_WOULD_BLOCK_ERROR;
- case ERR_ACCESS_DENIED:
- case ERR_NETWORK_ACCESS_DENIED:
- // For connect, this could be mapped to PR_ADDRESS_NOT_SUPPORTED_ERROR.
- return PR_NO_ACCESS_RIGHTS_ERROR;
- case ERR_NOT_IMPLEMENTED:
- return PR_NOT_IMPLEMENTED_ERROR;
- case ERR_INTERNET_DISCONNECTED: // Equivalent to ENETDOWN.
- return PR_NETWORK_UNREACHABLE_ERROR; // Best approximation.
- case ERR_CONNECTION_TIMED_OUT:
- case ERR_TIMED_OUT:
- return PR_IO_TIMEOUT_ERROR;
- case ERR_CONNECTION_RESET:
- return PR_CONNECT_RESET_ERROR;
- case ERR_CONNECTION_ABORTED:
- return PR_CONNECT_ABORTED_ERROR;
- case ERR_CONNECTION_REFUSED:
- return PR_CONNECT_REFUSED_ERROR;
- case ERR_ADDRESS_UNREACHABLE:
- return PR_HOST_UNREACHABLE_ERROR; // Also PR_NETWORK_UNREACHABLE_ERROR.
- case ERR_ADDRESS_INVALID:
- return PR_ADDRESS_NOT_AVAILABLE_ERROR;
- case ERR_NAME_NOT_RESOLVED:
- return PR_DIRECTORY_LOOKUP_ERROR;
- default:
- LOG(WARNING) << "MapErrorToNSS " << result
- << " mapped to PR_UNKNOWN_ERROR";
- return PR_UNKNOWN_ERROR;
- }
-}
-
// Do network I/O between the given buffer and the given socket.
// Return true if some I/O performed, false otherwise (error or ERR_IO_PENDING)
bool SSLClientSocketNSS::DoTransportIO() {
@@ -2191,7 +1981,7 @@ int SSLClientSocketNSS::DoHandshake() {
}
} else {
PRErrorCode prerr = PR_GetError();
- net_error = MapHandshakeError(prerr);
+ net_error = MapNSSHandshakeError(prerr);
// If not done, stay in this state
if (net_error == ERR_IO_PENDING) {
@@ -2580,7 +2370,7 @@ int SSLClientSocketNSS::DoPayloadRead() {
return ERR_IO_PENDING;
}
LeaveFunction("");
- rv = MapNSPRError(prerr);
+ rv = MapNSSError(prerr);
net_log_.AddEvent(NetLog::TYPE_SSL_READ_ERROR,
make_scoped_refptr(new SSLErrorParams(rv, prerr)));
return rv;
@@ -2601,7 +2391,7 @@ int SSLClientSocketNSS::DoPayloadWrite() {
return ERR_IO_PENDING;
}
LeaveFunction("");
- rv = MapNSPRError(prerr);
+ rv = MapNSSError(prerr);
net_log_.AddEvent(NetLog::TYPE_SSL_WRITE_ERROR,
make_scoped_refptr(new SSLErrorParams(rv, prerr)));
return rv;
diff --git a/net/socket/ssl_server_socket.h b/net/socket/ssl_server_socket.h
new file mode 100644
index 0000000..b689c71
--- /dev/null
+++ b/net/socket/ssl_server_socket.h
@@ -0,0 +1,53 @@
+// Copyright (c) 2010 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.
+
+#ifndef NET_SOCKET_SSL_SERVER_SOCKET_H_
+#define NET_SOCKET_SSL_SERVER_SOCKET_H_
+
+#include "base/basictypes.h"
+#include "net/base/completion_callback.h"
+#include "net/socket/socket.h"
+
+namespace base {
+class RSAPrivateKey;
+} // namespace base
+
+namespace net {
+
+class IOBuffer;
+struct SSLConfig;
+class X509Certificate;
+
+// SSLServerSocket takes an already connected socket and performs SSL on top of
+// it.
+//
+// This class is designed to work in a peer-to-peer connection and is not
+// intended to be used as a standalone SSL server.
+class SSLServerSocket : public Socket {
+ public:
+ virtual ~SSLServerSocket() {}
+
+ // Performs an SSL server handshake on the existing socket. The given socket
+ // must have already been connected.
+ //
+ // Accept either returns ERR_IO_PENDING, in which case the given callback
+ // will be called in the future with the real result, or it completes
+ // synchronously, returning the result immediately.
+ virtual int Accept(CompletionCallback* callback) = 0;
+};
+
+// Creates an SSL server socket using an already connected socket. A certificate
+// and private key needs to be provided.
+//
+// This created server socket will take ownership of |socket|. However |key|
+// is copied.
+// TODO(hclam): Defines ServerSocketFactory to create SSLServerSocket. This will
+// make mocking easier.
+SSLServerSocket* CreateSSLServerSocket(
+ Socket* socket, X509Certificate* certificate, base::RSAPrivateKey* key,
+ const SSLConfig& ssl_config);
+
+} // namespace net
+
+#endif // NET_SOCKET_SSL_SERVER_SOCKET_NSS_H_
diff --git a/net/socket/ssl_server_socket_nss.cc b/net/socket/ssl_server_socket_nss.cc
new file mode 100644
index 0000000..2e47fb8
--- /dev/null
+++ b/net/socket/ssl_server_socket_nss.cc
@@ -0,0 +1,677 @@
+// Copyright (c) 2010 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"
+
+#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/crypto/rsa_private_key.h"
+#include "base/nss_util_internal.h"
+#include "base/ref_counted.h"
+#include "net/base/io_buffer.h"
+#include "net/base/net_errors.h"
+#include "net/base/net_log.h"
+#include "net/ocsp/nss_ocsp.h"
+#include "net/socket/nss_ssl_util.h"
+#include "net/socket/ssl_error_params.h"
+
+static const int kRecvBufferSize = 4096;
+
+#define GotoState(s) next_handshake_state_ = s
+
+namespace net {
+
+SSLServerSocket* CreateSSLServerSocket(
+ Socket* socket, X509Certificate* cert, base::RSAPrivateKey* key,
+ const SSLConfig& ssl_config) {
+ return new SSLServerSocketNSS(socket, cert, key, ssl_config);
+}
+
+SSLServerSocketNSS::SSLServerSocketNSS(
+ Socket* transport_socket,
+ scoped_refptr<X509Certificate> cert,
+ base::RSAPrivateKey* key,
+ const SSLConfig& ssl_config)
+ : ALLOW_THIS_IN_INITIALIZER_LIST(buffer_send_callback_(
+ this, &SSLServerSocketNSS::BufferSendComplete)),
+ ALLOW_THIS_IN_INITIALIZER_LIST(buffer_recv_callback_(
+ this, &SSLServerSocketNSS::BufferRecvComplete)),
+ transport_send_busy_(false),
+ transport_recv_busy_(false),
+ user_accept_callback_(NULL),
+ user_read_callback_(NULL),
+ user_write_callback_(NULL),
+ nss_fd_(NULL),
+ nss_bufs_(NULL),
+ transport_socket_(transport_socket),
+ ssl_config_(ssl_config),
+ cert_(cert),
+ next_handshake_state_(STATE_NONE),
+ completed_handshake_(false) {
+ ssl_config_.false_start_enabled = false;
+ ssl_config_.ssl3_enabled = true;
+ ssl_config_.tls1_enabled = true;
+
+ // TODO(hclam): Need a better way to clone a key.
+ std::vector<uint8> key_bytes;
+ CHECK(key->ExportPrivateKey(&key_bytes));
+ key_.reset(base::RSAPrivateKey::CreateFromPrivateKeyInfo(key_bytes));
+ CHECK(key_.get());
+}
+
+SSLServerSocketNSS::~SSLServerSocketNSS() {
+ if (nss_fd_ != NULL) {
+ PR_Close(nss_fd_);
+ nss_fd_ = NULL;
+ }
+}
+
+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;
+#if !defined(OS_MACOSX) && !defined(OS_WIN)
+ // We must call EnsureOCSPInit() here, on the IO thread, to get the IO loop
+ // by MessageLoopForIO::current().
+ // X509Certificate::Verify() runs on a worker thread of CertVerifier.
+ EnsureOCSPInit();
+#endif
+
+ return OK;
+}
+
+int SSLServerSocketNSS::Accept(CompletionCallback* callback) {
+ net_log_.BeginEvent(NetLog::TYPE_SSL_ACCEPT, NULL);
+
+ int rv = Init();
+ if (rv != OK) {
+ LOG(ERROR) << "Failed to initialize NSS";
+ net_log_.EndEvent(NetLog::TYPE_SSL_ACCEPT, NULL);
+ return rv;
+ }
+
+ rv = InitializeSSLOptions();
+ if (rv != OK) {
+ LOG(ERROR) << "Failed to initialize SSL options";
+ net_log_.EndEvent(NetLog::TYPE_SSL_ACCEPT, NULL);
+ 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(net::OK);
+ if (rv == ERR_IO_PENDING) {
+ user_accept_callback_ = callback;
+ } else {
+ net_log_.EndEvent(NetLog::TYPE_SSL_ACCEPT, NULL);
+ }
+
+ return rv > OK ? OK : rv;
+}
+
+int SSLServerSocketNSS::Read(IOBuffer* buf, int buf_len,
+ CompletionCallback* callback) {
+ DCHECK(!user_read_callback_);
+ DCHECK(!user_accept_callback_);
+ DCHECK(!user_read_buf_);
+ DCHECK(nss_bufs_);
+
+ 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,
+ CompletionCallback* callback) {
+ DCHECK(!user_write_callback_);
+ DCHECK(!user_write_buf_);
+ DCHECK(nss_bufs_);
+
+ 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;
+}
+
+// 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::InitializeSSLOptions() {
+ // Transport connected, now hook it up to nss
+ // TODO(port): specify rx and tx buffer sizes separately
+ nss_fd_ = memio_CreateIOLayer(kRecvBufferSize);
+ 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;
+
+ 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;
+ }
+
+ rv = SSL_OptionSet(nss_fd_, SSL_ENABLE_SSL3, PR_TRUE);
+ if (rv != SECSuccess) {
+ LogFailedNSSFunction(net_log_, "SSL_OptionSet", "SSL_ENABLE_SSL3");
+ return ERR_UNEXPECTED;
+ }
+
+ rv = SSL_OptionSet(nss_fd_, SSL_ENABLE_TLS, ssl_config_.tls1_enabled);
+ if (rv != SECSuccess) {
+ LogFailedNSSFunction(net_log_, "SSL_OptionSet", "SSL_ENABLE_TLS");
+ return ERR_UNEXPECTED;
+ }
+
+ for (std::vector<uint16>::const_iterator it =
+ ssl_config_.disabled_cipher_suites.begin();
+ it != ssl_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_OptionSet(nss_fd_, SSL_NO_CACHE, PR_TRUE);
+ if (rv != SECSuccess) {
+ LogFailedNSSFunction(net_log_, "SSL_OptionSet", "SSL_NO_CACHE");
+ return ERR_UNEXPECTED;
+ }
+
+ rv = SSL_ConfigServerSessionIDCache(1024, 5, 5, NULL);
+ if (rv != SECSuccess) {
+ LogFailedNSSFunction(net_log_, "SSL_ConfigureServerSessionIDCache", "");
+ 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 (!cert_->GetDEREncoded(&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);
+
+ // Get a key of SECKEYPrivateKey* structure.
+ std::vector<uint8> key_vector;
+ if (!key_->ExportPrivateKey(&key_vector)) {
+ CERT_DestroyCertificate(cert);
+ return ERR_UNEXPECTED;
+ }
+
+ SECKEYPrivateKeyStr* private_key = NULL;
+ PK11SlotInfo *slot = base::GetDefaultNSSKeySlot();
+ 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();
+ rv = PK11_ImportDERPrivateKeyInfoAndReturnKey(
+ slot, &der_private_key_info, NULL, NULL, PR_FALSE, PR_FALSE,
+ KU_DIGITAL_SIGNATURE, &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;
+}
+
+// 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;
+ memio_GetWriteParams(nss_bufs_, &buf1, &len1, &buf2, &len2);
+ const unsigned int 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, len,
+ &buffer_send_callback_);
+ 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_, nb, &buffer_recv_callback_);
+ 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);
+}
+
+void SSLServerSocketNSS::OnSendComplete(int result) {
+ if (next_handshake_state_ == STATE_HANDSHAKE) {
+ // In handshake phase.
+ OnHandshakeIOComplete(result);
+ return;
+ }
+
+ if (!user_write_buf_ || !completed_handshake_)
+ return;
+
+ int rv = DoWriteLoop(result);
+ if (rv != ERR_IO_PENDING)
+ DoWriteCallback(rv);
+}
+
+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) {
+ net_log_.EndEvent(net::NetLog::TYPE_SSL_ACCEPT, NULL);
+ if (user_accept_callback_)
+ DoAcceptCallback(rv);
+ }
+}
+
+void SSLServerSocketNSS::DoAcceptCallback(int rv) {
+ DCHECK_NE(rv, ERR_IO_PENDING);
+
+ CompletionCallback* c = user_accept_callback_;
+ user_accept_callback_ = NULL;
+ c->Run(rv > OK ? OK : rv);
+}
+
+void SSLServerSocketNSS::DoReadCallback(int rv) {
+ DCHECK(rv != ERR_IO_PENDING);
+ DCHECK(user_read_callback_);
+
+ // Since Run may result in Read being called, clear |user_read_callback_|
+ // up front.
+ CompletionCallback* c = user_read_callback_;
+ user_read_callback_ = NULL;
+ user_read_buf_ = NULL;
+ user_read_buf_len_ = 0;
+ c->Run(rv);
+}
+
+void SSLServerSocketNSS::DoWriteCallback(int rv) {
+ DCHECK(rv != ERR_IO_PENDING);
+ DCHECK(user_write_callback_);
+
+ // Since Run may result in Write being called, clear |user_write_callback_|
+ // up front.
+ CompletionCallback* c = user_write_callback_;
+ user_write_callback_ = NULL;
+ user_write_buf_ = NULL;
+ user_write_buf_len_ = 0;
+ c->Run(rv);
+}
+
+// Do network I/O between the given buffer and the given 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 nsent = BufferSend();
+ int nreceived = BufferRecv();
+ network_moved = (nsent > 0 || nreceived >= 0);
+ }
+ 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,
+ make_scoped_refptr(new SSLErrorParams(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,
+ make_scoped_refptr(new SSLErrorParams(rv, prerr)));
+ return rv;
+}
+
+int SSLServerSocketNSS::DoHandshakeLoop(int last_io_result) {
+ bool network_moved;
+ 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_NONE:
+ // we're just pumping data between the buffer and the network
+ break;
+ case STATE_HANDSHAKE:
+ rv = DoHandshake();
+ break;
+ default:
+ rv = ERR_UNEXPECTED;
+ LOG(DFATAL) << "unexpected state " << state;
+ break;
+ }
+
+ // Do the actual network I/O
+ network_moved = DoTransportIO();
+ } while ((rv != ERR_IO_PENDING || network_moved) &&
+ 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,
+ make_scoped_refptr(new SSLErrorParams(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(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,
+ make_scoped_refptr(new SSLErrorParams(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 = net::OK;
+ SECStatus rv = SSL_ForceHandshake(nss_fd_);
+
+ if (rv == SECSuccess) {
+ completed_handshake_ = true;
+ } else {
+ PRErrorCode prerr = PR_GetError();
+ net_error = MapNSSHandshakeError(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,
+ make_scoped_refptr(new SSLErrorParams(net_error, prerr)));
+ }
+ }
+ return net_error;
+}
+
+} // namespace net
diff --git a/net/socket/ssl_server_socket_nss.h b/net/socket/ssl_server_socket_nss.h
new file mode 100644
index 0000000..3883c9b
--- /dev/null
+++ b/net/socket/ssl_server_socket_nss.h
@@ -0,0 +1,133 @@
+// Copyright (c) 2006-2009 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.
+
+#ifndef NET_SOCKET_SSL_SERVER_SOCKET_NSS_H_
+#define NET_SOCKET_SSL_SERVER_SOCKET_NSS_H_
+#pragma once
+
+#include <certt.h>
+#include <keyt.h>
+#include <nspr.h>
+#include <nss.h>
+
+#include "base/scoped_ptr.h"
+#include "net/base/completion_callback.h"
+#include "net/base/host_port_pair.h"
+#include "net/base/net_log.h"
+#include "net/base/nss_memio.h"
+#include "net/base/ssl_config_service.h"
+#include "net/socket/ssl_server_socket.h"
+
+namespace net {
+
+class SSLServerSocketNSS : public SSLServerSocket {
+ public:
+ // This object takes ownership of the following parameters:
+ // |socket| - A socket that is already connected.
+ // |cert| - The certificate to be used by the server.
+ //
+ // The following parameters are copied in the constructor.
+ // |ssl_config| - Options for SSL socket.
+ // |key| - The private key used by the server.
+ SSLServerSocketNSS(Socket* transport_socket,
+ scoped_refptr<X509Certificate> cert,
+ base::RSAPrivateKey* key,
+ const SSLConfig& ssl_config);
+ virtual ~SSLServerSocketNSS();
+
+ // SSLServerSocket implementation.
+ virtual int Accept(CompletionCallback* callback);
+ virtual int Read(IOBuffer* buf, int buf_len,
+ CompletionCallback* callback);
+ virtual int Write(IOBuffer* buf, int buf_len,
+ CompletionCallback* callback);
+ virtual bool SetReceiveBufferSize(int32 size) { return false; }
+ virtual bool SetSendBufferSize(int32 size) { return false; }
+
+ private:
+ virtual int Init();
+
+ 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 DoPayloadWrite();
+ int DoPayloadRead();
+
+ int DoHandshakeLoop(int last_io_result);
+ int DoReadLoop(int result);
+ int DoWriteLoop(int result);
+ int DoHandshake();
+ void DoAcceptCallback(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);
+
+ // Members used to send and receive buffer.
+ CompletionCallbackImpl<SSLServerSocketNSS> buffer_send_callback_;
+ CompletionCallbackImpl<SSLServerSocketNSS> buffer_recv_callback_;
+ bool transport_send_busy_;
+ bool transport_recv_busy_;
+
+ scoped_refptr<IOBuffer> recv_buffer_;
+
+ BoundNetLog net_log_;
+
+ CompletionCallback* user_accept_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_;
+
+ // Socket for sending and receiving data.
+ scoped_ptr<Socket> transport_socket_;
+
+ // Options for the SSL socket.
+ // TODO(hclam): This memeber is currently not used. Should make use of this
+ // member to configure the socket.
+ SSLConfig ssl_config_;
+
+ // Certificate for the server.
+ scoped_refptr<X509Certificate> cert_;
+
+ // Private key used by the server.
+ scoped_ptr<base::RSAPrivateKey> key_;
+
+ enum State {
+ STATE_NONE,
+ STATE_HANDSHAKE,
+ };
+ State next_handshake_state_;
+ bool completed_handshake_;
+
+ DISALLOW_COPY_AND_ASSIGN(SSLServerSocketNSS);
+};
+
+} // namespace net
+
+#endif // NET_SOCKET_SSL_SERVER_SOCKET_NSS_H_
diff --git a/net/socket/ssl_server_socket_unittest.cc b/net/socket/ssl_server_socket_unittest.cc
new file mode 100644
index 0000000..781a3f4
--- /dev/null
+++ b/net/socket/ssl_server_socket_unittest.cc
@@ -0,0 +1,369 @@
+// Copyright (c) 2010 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.
+
+// This test suite uses SSLClientSocket to test the implementation of
+// SSLServerSocket. In order to establish connections between the sockets
+// we need two additional classes:
+// 1. FakeSocket
+// Connects SSL socket to FakeDataChannel. This class is just a stub.
+//
+// 2. FakeDataChannel
+// Implements the actual exchange of data between two FakeSockets.
+//
+// Implementations of these two classes are included in this file.
+
+#include "net/socket/ssl_server_socket.h"
+
+#include <queue>
+
+#include "base/crypto/rsa_private_key.h"
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/nss_util.h"
+#include "base/path_service.h"
+#include "net/base/address_list.h"
+#include "net/base/cert_verifier.h"
+#include "net/base/host_port_pair.h"
+#include "net/base/io_buffer.h"
+#include "net/base/net_errors.h"
+#include "net/base/net_log.h"
+#include "net/base/ssl_config_service.h"
+#include "net/base/x509_certificate.h"
+#include "net/socket/client_socket.h"
+#include "net/socket/client_socket_factory.h"
+#include "net/socket/socket_test_util.h"
+#include "net/socket/ssl_client_socket.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/platform_test.h"
+
+namespace net {
+
+namespace {
+
+class FakeDataChannel {
+ public:
+ FakeDataChannel() : read_callback_(NULL), read_buf_len_(0) {
+ }
+
+ virtual int Read(IOBuffer* buf, int buf_len,
+ CompletionCallback* callback) {
+ if (data_.empty()) {
+ read_callback_ = callback;
+ read_buf_ = buf;
+ read_buf_len_ = buf_len;
+ return net::ERR_IO_PENDING;
+ }
+ return PropogateData(buf, buf_len);
+ }
+
+ virtual int Write(IOBuffer* buf, int buf_len,
+ CompletionCallback* callback) {
+ data_.push(new net::DrainableIOBuffer(buf, buf_len));
+ DoReadCallback();
+ return buf_len;
+ }
+
+ private:
+ void DoReadCallback() {
+ if (!read_callback_)
+ return;
+
+ int copied = PropogateData(read_buf_, read_buf_len_);
+ net::CompletionCallback* callback = read_callback_;
+ read_callback_ = NULL;
+ read_buf_ = NULL;
+ read_buf_len_ = 0;
+ callback->Run(copied);
+ }
+
+ int PropogateData(scoped_refptr<net::IOBuffer> read_buf, int read_buf_len) {
+ scoped_refptr<net::DrainableIOBuffer> buf = data_.front();
+ int copied = std::min(buf->BytesRemaining(), read_buf_len);
+ memcpy(read_buf->data(), buf->data(), copied);
+ buf->DidConsume(copied);
+
+ if (!buf->BytesRemaining())
+ data_.pop();
+ return copied;
+ }
+
+ net::CompletionCallback* read_callback_;
+ scoped_refptr<net::IOBuffer> read_buf_;
+ int read_buf_len_;
+
+ std::queue<scoped_refptr<net::DrainableIOBuffer> > data_;
+
+ DISALLOW_COPY_AND_ASSIGN(FakeDataChannel);
+};
+
+class FakeSocket : public ClientSocket {
+ public:
+ FakeSocket(FakeDataChannel* incoming_channel,
+ FakeDataChannel* outgoing_channel)
+ : incoming_(incoming_channel),
+ outgoing_(outgoing_channel) {
+ }
+
+ virtual ~FakeSocket() {
+
+ }
+
+ virtual int Read(IOBuffer* buf, int buf_len,
+ CompletionCallback* callback) {
+ return incoming_->Read(buf, buf_len, callback);
+ }
+
+ virtual int Write(IOBuffer* buf, int buf_len,
+ CompletionCallback* callback) {
+ return outgoing_->Write(buf, buf_len, callback);
+ }
+
+ virtual bool SetReceiveBufferSize(int32 size) {
+ return true;
+ }
+
+ virtual bool SetSendBufferSize(int32 size) {
+ return true;
+ }
+
+ virtual int Connect(CompletionCallback* callback) {
+ return net::OK;
+ }
+
+ virtual void Disconnect() {}
+
+ virtual bool IsConnected() const {
+ return true;
+ }
+
+ virtual bool IsConnectedAndIdle() const {
+ return true;
+ }
+
+ virtual int GetPeerAddress(AddressList* address) const {
+ net::IPAddressNumber ip_address(4);
+ *address = net::AddressList(ip_address, 0, false);
+ return net::OK;
+ }
+
+ virtual const BoundNetLog& NetLog() const {
+ return net_log_;
+ }
+
+ virtual void SetSubresourceSpeculation() {}
+ virtual void SetOmniboxSpeculation() {}
+
+ virtual bool WasEverUsed() const {
+ return true;
+ }
+
+ virtual bool UsingTCPFastOpen() const {
+ return false;
+ }
+
+ private:
+ net::BoundNetLog net_log_;
+ FakeDataChannel* incoming_;
+ FakeDataChannel* outgoing_;
+
+ DISALLOW_COPY_AND_ASSIGN(FakeSocket);
+};
+
+} // namespace
+
+// Verify the correctness of the test helper classes first.
+TEST(FakeSocketTest, DataTransfer) {
+ // Establish channels between two sockets.
+ FakeDataChannel channel_1;
+ FakeDataChannel channel_2;
+ FakeSocket client(&channel_1, &channel_2);
+ FakeSocket server(&channel_2, &channel_1);
+
+ const char kTestData[] = "testing123";
+ const int kTestDataSize = strlen(kTestData);
+ const int kReadBufSize = 1024;
+ scoped_refptr<net::IOBuffer> write_buf = new net::StringIOBuffer(kTestData);
+ scoped_refptr<net::IOBuffer> read_buf = new net::IOBuffer(kReadBufSize);
+
+ // Write then read.
+ EXPECT_EQ(kTestDataSize, server.Write(write_buf, kTestDataSize, NULL));
+ EXPECT_EQ(kTestDataSize, client.Read(read_buf, kReadBufSize, NULL));
+ EXPECT_EQ(0, memcmp(kTestData, read_buf->data(), kTestDataSize));
+
+ // Read then write.
+ TestCompletionCallback callback;
+ EXPECT_EQ(net::ERR_IO_PENDING,
+ server.Read(read_buf, kReadBufSize, &callback));
+ EXPECT_EQ(kTestDataSize, client.Write(write_buf, kTestDataSize, NULL));
+ EXPECT_EQ(kTestDataSize, callback.WaitForResult());
+ EXPECT_EQ(0, memcmp(kTestData, read_buf->data(), kTestDataSize));
+}
+
+class SSLServerSocketTest : public PlatformTest {
+ public:
+ SSLServerSocketTest()
+ : socket_factory_(net::ClientSocketFactory::GetDefaultFactory()) {
+ }
+
+ protected:
+ void Initialize() {
+ FakeSocket* fake_client_socket = new FakeSocket(&channel_1_, &channel_2_);
+ FakeSocket* fake_server_socket = new FakeSocket(&channel_2_, &channel_1_);
+
+ FilePath certs_dir;
+ PathService::Get(base::DIR_SOURCE_ROOT, &certs_dir);
+ certs_dir = certs_dir.AppendASCII("net");
+ certs_dir = certs_dir.AppendASCII("data");
+ certs_dir = certs_dir.AppendASCII("ssl");
+ certs_dir = certs_dir.AppendASCII("certificates");
+
+ FilePath cert_path = certs_dir.AppendASCII("unittest.selfsigned.der");
+ std::string cert_der;
+ ASSERT_TRUE(file_util::ReadFileToString(cert_path, &cert_der));
+
+ scoped_refptr<net::X509Certificate> cert =
+ X509Certificate::CreateFromBytes(cert_der.data(), cert_der.size());
+
+ FilePath key_path = certs_dir.AppendASCII("unittest.key.bin");
+ std::string key_string;
+ ASSERT_TRUE(file_util::ReadFileToString(key_path, &key_string));
+ std::vector<uint8> key_vector(
+ reinterpret_cast<const uint8*>(key_string.data()),
+ reinterpret_cast<const uint8*>(key_string.data() +
+ key_string.length()));
+
+ scoped_ptr<base::RSAPrivateKey> private_key(
+ base::RSAPrivateKey::CreateFromPrivateKeyInfo(key_vector));
+
+ net::SSLConfig ssl_config;
+ ssl_config.false_start_enabled = false;
+ ssl_config.snap_start_enabled = false;
+ ssl_config.ssl3_enabled = true;
+ ssl_config.tls1_enabled = true;
+ ssl_config.session_resume_disabled = true;
+
+ // Certificate provided by the host doesn't need authority.
+ net::SSLConfig::CertAndStatus cert_and_status;
+ cert_and_status.cert_status = net::ERR_CERT_AUTHORITY_INVALID;
+ cert_and_status.cert = cert;
+ ssl_config.allowed_bad_certs.push_back(cert_and_status);
+
+ net::HostPortPair host_and_pair("unittest", 0);
+ client_socket_.reset(
+ socket_factory_->CreateSSLClientSocket(
+ fake_client_socket, host_and_pair, ssl_config, NULL,
+ &cert_verifier_));
+ server_socket_.reset(net::CreateSSLServerSocket(fake_server_socket,
+ cert, private_key.get(),
+ net::SSLConfig()));
+ }
+
+ FakeDataChannel channel_1_;
+ FakeDataChannel channel_2_;
+ scoped_ptr<net::SSLClientSocket> client_socket_;
+ scoped_ptr<net::SSLServerSocket> server_socket_;
+ net::ClientSocketFactory* socket_factory_;
+ net::CertVerifier cert_verifier_;
+};
+
+// SSLServerSocket is only implemented using NSS.
+#if defined(USE_NSS) || defined(OS_WIN) || defined(OS_MACOSX)
+
+// This test only executes creation of client and server sockets. This is to
+// test that creation of sockets doesn't crash and have minimal code to run
+// under valgrind in order to help debugging memory problems.
+TEST_F(SSLServerSocketTest, Initialize) {
+ Initialize();
+}
+
+// This test executes Connect() of SSLClientSocket and Accept() of
+// SSLServerSocket to make sure handshaking between the two sockets are
+// completed successfully.
+TEST_F(SSLServerSocketTest, Handshake) {
+ Initialize();
+
+ if (!base::CheckNSSVersion("3.12.8"))
+ return;
+
+ TestCompletionCallback connect_callback;
+ TestCompletionCallback accept_callback;
+
+ int server_ret = server_socket_->Accept(&accept_callback);
+ EXPECT_TRUE(server_ret == net::OK || server_ret == net::ERR_IO_PENDING);
+
+ int client_ret = client_socket_->Connect(&connect_callback);
+ EXPECT_TRUE(client_ret == net::OK || client_ret == net::ERR_IO_PENDING);
+
+ if (client_ret == net::ERR_IO_PENDING) {
+ EXPECT_EQ(net::OK, connect_callback.WaitForResult());
+ }
+ if (server_ret == net::ERR_IO_PENDING) {
+ EXPECT_EQ(net::OK, accept_callback.WaitForResult());
+ }
+}
+
+TEST_F(SSLServerSocketTest, DataTransfer) {
+ Initialize();
+
+ if (!base::CheckNSSVersion("3.12.8"))
+ return;
+
+ TestCompletionCallback connect_callback;
+ TestCompletionCallback accept_callback;
+
+ // Establish connection.
+ int client_ret = client_socket_->Connect(&connect_callback);
+ EXPECT_TRUE(client_ret == net::OK || client_ret == net::ERR_IO_PENDING);
+
+ int server_ret = server_socket_->Accept(&accept_callback);
+ EXPECT_TRUE(server_ret == net::OK || server_ret == net::ERR_IO_PENDING);
+
+ if (client_ret == net::ERR_IO_PENDING) {
+ EXPECT_EQ(net::OK, connect_callback.WaitForResult());
+ }
+ if (server_ret == net::ERR_IO_PENDING) {
+ EXPECT_EQ(net::OK, accept_callback.WaitForResult());
+ }
+
+ const int kReadBufSize = 1024;
+ scoped_refptr<net::StringIOBuffer> write_buf =
+ new net::StringIOBuffer("testing123");
+ scoped_refptr<net::IOBuffer> read_buf = new net::IOBuffer(kReadBufSize);
+
+ // Write then read.
+ TestCompletionCallback write_callback;
+ TestCompletionCallback read_callback;
+ server_ret = server_socket_->Write(write_buf, write_buf->size(),
+ &write_callback);
+ EXPECT_TRUE(server_ret > 0 || server_ret == net::ERR_IO_PENDING);
+ client_ret = client_socket_->Read(read_buf, kReadBufSize, &read_callback);
+ EXPECT_TRUE(client_ret > 0 || client_ret == net::ERR_IO_PENDING);
+
+ if (server_ret == net::ERR_IO_PENDING) {
+ EXPECT_GT(write_callback.WaitForResult(), 0);
+ }
+ if (client_ret == net::ERR_IO_PENDING) {
+ EXPECT_GT(read_callback.WaitForResult(), 0);
+ }
+ EXPECT_EQ(0, memcmp(write_buf->data(), read_buf->data(), write_buf->size()));
+
+ // Read then write.
+ write_buf = new net::StringIOBuffer("hello123");
+ server_ret = server_socket_->Read(read_buf, kReadBufSize, &read_callback);
+ EXPECT_TRUE(server_ret > 0 || server_ret == net::ERR_IO_PENDING);
+ client_ret = client_socket_->Write(write_buf, write_buf->size(),
+ &write_callback);
+ EXPECT_TRUE(client_ret > 0 || client_ret == net::ERR_IO_PENDING);
+
+ if (server_ret == net::ERR_IO_PENDING) {
+ EXPECT_GT(read_callback.WaitForResult(), 0);
+ }
+ if (client_ret == net::ERR_IO_PENDING) {
+ EXPECT_GT(write_callback.WaitForResult(), 0);
+ }
+ EXPECT_EQ(0, memcmp(write_buf->data(), read_buf->data(), write_buf->size()));
+}
+#endif
+
+} // namespace net