Index: net/socket/ssl_client_socket_tls_srp_unittest.cc |
diff --git a/net/socket/ssl_client_socket_tls_srp_unittest.cc b/net/socket/ssl_client_socket_tls_srp_unittest.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..01e72137830d313ee438d05737157fc1d2eda33c |
--- /dev/null |
+++ b/net/socket/ssl_client_socket_tls_srp_unittest.cc |
@@ -0,0 +1,312 @@ |
+// 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 <arpa/inet.h> |
+#include <netinet/tcp.h> |
+#include <stdarg.h> |
+#include <sys/socket.h> |
+#include <sys/types.h> |
+ |
+#include <sslt.h> |
+ |
+#include <vector> |
+#include <string> |
+ |
+#include "base/eintr_wrapper.h" |
+#include "base/base64.h" |
+#include "base/file_path.h" |
+#include "base/file_util.h" |
+#include "base/path_service.h" |
+#include "base/process_util.h" |
+#include "base/string_util.h" |
+#include "base/values.h" |
+#include "net/base/io_buffer.h" |
+#include "net/base/net_errors.h" |
+#include "net/base/net_log.h" |
+#include "net/base/net_log_unittest.h" |
+#include "net/base/ssl_config_service.h" |
+#include "net/base/test_completion_callback.h" |
+#include "net/socket/client_socket_factory.h" |
+#include "net/socket/ssl_client_socket.h" |
+#include "net/socket/ssl_client_socket_nss.h" |
+#include "net/socket/ssl_host_info.h" |
+#include "net/socket/tcp_client_socket.h" |
+#include "testing/gtest/include/gtest/gtest.h" |
+#include "testing/gtest/include/gtest/gtest-spi.h" |
+#include "testing/platform_test.h" |
+ |
+namespace net { |
+ |
+// TestSSLHostInfo is an SSLHostInfo which stores a single state in memory and |
+// pretends that certificate verification always succeeds. |
+class TestSSLHostInfo : public SSLHostInfo { |
+ public: |
+ explicit TestSSLHostInfo(CertVerifier* cert_verifier) |
+ : SSLHostInfo("example.com", kDefaultSSLConfig, cert_verifier) { |
+ if (!saved_.empty()) |
+ Parse(saved_); |
+ cert_verification_complete_ = true; |
+ cert_verification_error_ = OK; |
+ } |
+ |
+ virtual void Start() { |
+ } |
+ |
+ virtual int WaitForDataReady(CompletionCallback*) { return OK; } |
+ |
+ virtual void Persist() { |
+ saved_ = Serialize(); |
+ } |
+ |
+ static void Reset() { |
+ saved_.clear(); |
+ } |
+ |
+ private: |
+ static SSLConfig kDefaultSSLConfig; |
+ static std::string saved_; |
+}; |
+ |
+std::string TestSSLHostInfo::saved_; |
+ |
+SSLConfig TestSSLHostInfo::kDefaultSSLConfig; |
+ |
+class SSLClientSocketTLSSRPTest : public PlatformTest { |
+ public: |
+ SSLClientSocketTLSSRPTest() |
+ : child_(base::kNullProcessHandle), |
+ socket_factory_(ClientSocketFactory::GetDefaultFactory()), |
+ log_(CapturingNetLog::kUnbounded), |
+ expected_exit_code_(0) |
+ { |
+ TestSSLHostInfo::Reset(); |
+ } |
+ |
+ virtual void TearDown() { |
+ if (child_ != base::kNullProcessHandle) { |
+ int exit_code; |
+ EXPECT_TRUE(base::WaitForExitCode(child_, &exit_code)); |
+ EXPECT_EQ(expected_exit_code_, exit_code); |
+ } |
+ } |
+ |
+ protected: |
+ void StartTLSSRPServer(const char* arg, ...) { |
+ FilePath dir_exe; |
+ PathService::Get(base::DIR_EXE, &dir_exe); |
+ FilePath helper_binary = dir_exe.Append("openssl_helper"); |
+ |
+ std::vector<std::string> args; |
+ args.push_back(helper_binary.value()); |
+ |
+ args.push_back("tls-srp"); |
+ args.push_back("--srpv-file"); |
+ args.push_back(GetDefaultSRPVFilePath()); |
+ |
+ // Additional arguments can override the --srpv-file set above. |
+ va_list ap; |
+ va_start(ap, arg); |
+ while (arg) { |
+ args.push_back(arg); |
+ arg = va_arg(ap, const char*); |
+ } |
+ va_end(ap); |
+ |
+ const int listener = socket(AF_INET, SOCK_STREAM, 0); |
+ ASSERT_GE(listener, 0); |
+ struct sockaddr_in sin; |
+ memset(&sin, 0, sizeof(sin)); |
+ sin.sin_family = PF_INET; |
+ ASSERT_EQ(0, bind(listener, (struct sockaddr*) &sin, sizeof(sin))); |
+ socklen_t socklen = sizeof(remote_); |
+ ASSERT_EQ(0, getsockname(listener, (struct sockaddr*) &remote_, &socklen)); |
+ ASSERT_EQ(sizeof(remote_), socklen); |
+ ASSERT_EQ(0, listen(listener, 1)); |
+ |
+ base::file_handle_mapping_vector mapping; |
+ // The listening socket is installed as the child's fd 3. |
+ mapping.push_back(std::make_pair(listener, 3)); |
+ base::LaunchApp(args, mapping, false /* don't wait */, &child_); |
+ ASSERT_EQ(0, HANDLE_EINTR(close(listener))); |
+ } |
+ |
+ // GetDefaultSRPVFilePath returns the path to the default test SRPV file, |
+ // ok.srpv |
+ std::string GetDefaultSRPVFilePath() { |
+ FilePath path; |
+ PathService::Get(base::DIR_SOURCE_ROOT, &path); |
+ path = path.Append("net"); |
+ path = path.Append("data"); |
+ path = path.Append("ssl"); |
+ path = path.Append("certificates"); |
+ path = path.Append("ok.srpv"); |
+ return path.value(); |
+ } |
+ |
+ // LoadDefaultCert returns the DER encoded default certificate. |
+ std::string LoadDefaultCert() { |
+ FilePath path; |
+ PathService::Get(base::DIR_SOURCE_ROOT, &path); |
+ path = path.Append("net"); |
+ path = path.Append("data"); |
+ path = path.Append("ssl"); |
+ path = path.Append("certificates"); |
+ path = path.Append("ok_cert.pem"); |
+ |
+ std::string pem; |
+ bool r = file_util::ReadFileToString(path, &pem); |
+ CHECK(r) << "failed to read " << path.value(); |
+ |
+ static const char kStartMarker[] = "-----BEGIN CERTIFICATE-----\n"; |
+ static const char kEndMarker[] = "-----END CERTIFICATE-----\n"; |
+ |
+ std::string::size_type i = pem.find(kStartMarker); |
+ std::string::size_type j = pem.find(kEndMarker); |
+ CHECK(i != std::string::npos); |
+ CHECK(j != std::string::npos); |
+ CHECK_GT(j, i); |
+ i += sizeof(kStartMarker) - 1; |
+ |
+ std::string b64data = pem.substr(i, j - i); |
+ ReplaceSubstringsAfterOffset(&b64data, 0 /* start offset */, "\n", ""); |
+ |
+ std::string der; |
+ base::Base64Decode(b64data, &der); |
+ return der; |
+ } |
+ |
+ void SetupSSLConfig() { |
+ if (ssl_config_.allowed_bad_certs.size()) |
+ return; |
+ const std::string der = LoadDefaultCert(); |
+ SSLConfig::CertAndStatus cert_and_status; |
+ cert_and_status.cert = |
+ X509Certificate::CreateFromBytes(der.data(), der.size()); |
+ cert_and_status.cert_status = ERR_CERT_AUTHORITY_INVALID; |
+ |
+ ssl_config_.allowed_bad_certs.push_back(cert_and_status); |
+ } |
+ |
+ // PerformConnection makes an SSL connection to the openssl_helper binary and |
+ // does a ping-pong test to check the the SSL socket is working correctly. |
+ void PerformConnection(std::string username = "", |
+ std::string password = "", |
+ bool fail = false, |
+ int expected_net_error = OK) { |
+ client_ = socket(AF_INET, SOCK_STREAM, 0); |
+ ASSERT_LE(0, client_); |
+ ASSERT_EQ( |
+ 0, connect(client_, (struct sockaddr*) &remote_, sizeof(remote_))); |
+ int on = 1; |
+ setsockopt(client_, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)); |
+ |
+ SetupSSLConfig(); |
+ log_.Clear(); |
+ |
+ std::vector<uint8> localhost; |
+ localhost.push_back(127); |
+ localhost.push_back(0); |
+ localhost.push_back(0); |
+ localhost.push_back(1); |
+ AddressList addr_list(localhost, 443, false); |
+ TCPClientSocket* transport = new TCPClientSocket( |
+ addr_list, &log_, NetLog::Source()); |
+ |
+ if (!username.empty()) { |
+ ssl_config_.use_tls_auth = true; |
+ ssl_config_.tls_username = username; |
+ ssl_config_.tls_password = password; |
+ ssl_config_.ssl3_enabled = false; |
+ ssl_config_.tls1_enabled = true; |
+ } |
+ |
+ transport->AdoptSocket(client_); |
+ scoped_ptr<SSLClientSocket> sock( |
+ socket_factory_->CreateSSLClientSocket( |
+ transport, HostPortPair("example.com", 443), ssl_config_, |
+ new TestSSLHostInfo(&cert_verifier_), &cert_verifier_)); |
+ |
+ TestCompletionCallback callback; |
+ int rv = sock->Connect(&callback); |
+ if (rv != OK) { |
+ ASSERT_EQ(ERR_IO_PENDING, rv); |
+ EXPECT_FALSE(sock->IsConnected()); |
+ rv = callback.WaitForResult(); |
+ EXPECT_EQ(expected_net_error, rv); // default expected_net_error is OK |
+ return; |
+ } |
+ EXPECT_TRUE(sock->IsConnected()); |
+ |
+ static const char request_text[] = "whoami"; |
+ scoped_refptr<IOBuffer> request_buffer = |
+ new IOBuffer(arraysize(request_text) - 1); |
+ memcpy(request_buffer->data(), request_text, arraysize(request_text) - 1); |
+ rv = sock->Write(request_buffer, arraysize(request_text), &callback); |
+ if (rv < 0) { |
+ ASSERT_EQ(ERR_IO_PENDING, rv); |
+ rv = callback.WaitForResult(); |
+ } |
+ EXPECT_EQ(7, rv); |
+ |
+ scoped_refptr<IOBuffer> reply_buffer = new IOBuffer(8); |
+ rv = sock->Read(reply_buffer, 8, &callback); |
+ if (rv < 0) { |
+ ASSERT_EQ(ERR_IO_PENDING, rv); |
+ rv = callback.WaitForResult(); |
+ } |
+ |
+ std::string exp_response = (ssl_config_.use_tls_auth) ? |
+ ssl_config_.tls_username : "not-using-tls-srp"; |
+ |
+ EXPECT_EQ((int)exp_response.size(), rv); |
+ EXPECT_TRUE(memcmp(reply_buffer->data(), exp_response.data(), |
+ exp_response.size()) == 0); |
+ |
+ next_proto_status_ = sock->GetNextProto(&next_proto_); |
+ |
+ sock->Disconnect(); |
+ } |
+ |
+ base::ProcessHandle child_; |
+ CertVerifier cert_verifier_; |
+ ClientSocketFactory* const socket_factory_; |
+ struct sockaddr_in remote_; |
+ int client_; |
+ SSLConfig ssl_config_; |
+ CapturingNetLog log_; |
+ SSLClientSocket::NextProtoStatus next_proto_status_; |
+ std::string next_proto_; |
+ int expected_exit_code_; |
+}; |
+ |
+TEST_F(SSLClientSocketTLSSRPTest, NoCredentials) { |
+ StartTLSSRPServer(NULL); |
+ // Not a TLS-SRP connection. |
+ PerformConnection("", "", true, ERR_SSL_UNKNOWN_PSK_IDENTITY_ALERT); |
+ expected_exit_code_ = 1; |
+} |
+ |
+TEST_F(SSLClientSocketTLSSRPTest, GoodCredentials) { |
+ StartTLSSRPServer(NULL); |
+ PerformConnection("user", "secret"); |
+} |
+ |
+// TODO(sqs): The current openssl_helper terminates the connection upon seeing |
+// a username it doesn't know. It should instead either (1) make up (s,v) and |
+// play along, or (2) send unknown_psk_identity. When it's fixed, the net error |
+// below will need to change. |
+TEST_F(SSLClientSocketTLSSRPTest, BadCredentials) { |
+ StartTLSSRPServer(NULL); |
+ PerformConnection("baduser", "secret", true, |
+ ERR_SSL_UNKNOWN_PSK_IDENTITY_ALERT); |
+ expected_exit_code_ = 1; |
+} |
+ |
+TEST_F(SSLClientSocketTLSSRPTest, GoodUsernameBadPassword) { |
+ StartTLSSRPServer(NULL); |
+ PerformConnection("user", "badsecret", true, ERR_SSL_BAD_RECORD_MAC_ALERT); |
+ expected_exit_code_ = 1; |
+} |
+ |
+} // namespace net |