OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include <arpa/inet.h> |
| 6 #include <netinet/tcp.h> |
| 7 #include <stdarg.h> |
| 8 #include <sys/socket.h> |
| 9 #include <sys/types.h> |
| 10 |
| 11 #include <sslt.h> |
| 12 |
| 13 #include <vector> |
| 14 #include <string> |
| 15 |
| 16 #include "base/eintr_wrapper.h" |
| 17 #include "base/base64.h" |
| 18 #include "base/file_path.h" |
| 19 #include "base/file_util.h" |
| 20 #include "base/path_service.h" |
| 21 #include "base/process_util.h" |
| 22 #include "base/string_util.h" |
| 23 #include "base/values.h" |
| 24 #include "net/base/io_buffer.h" |
| 25 #include "net/base/net_errors.h" |
| 26 #include "net/base/net_log.h" |
| 27 #include "net/base/net_log_unittest.h" |
| 28 #include "net/base/ssl_config_service.h" |
| 29 #include "net/base/test_completion_callback.h" |
| 30 #include "net/socket/client_socket_factory.h" |
| 31 #include "net/socket/ssl_client_socket.h" |
| 32 #include "net/socket/ssl_client_socket_nss.h" |
| 33 #include "net/socket/ssl_host_info.h" |
| 34 #include "net/socket/tcp_client_socket.h" |
| 35 #include "testing/gtest/include/gtest/gtest.h" |
| 36 #include "testing/gtest/include/gtest/gtest-spi.h" |
| 37 #include "testing/platform_test.h" |
| 38 |
| 39 namespace net { |
| 40 |
| 41 // TestSSLHostInfo is an SSLHostInfo which stores a single state in memory and |
| 42 // pretends that certificate verification always succeeds. |
| 43 class TestSSLHostInfo : public SSLHostInfo { |
| 44 public: |
| 45 explicit TestSSLHostInfo(CertVerifier* cert_verifier) |
| 46 : SSLHostInfo("example.com", kDefaultSSLConfig, cert_verifier) { |
| 47 if (!saved_.empty()) |
| 48 Parse(saved_); |
| 49 cert_verification_complete_ = true; |
| 50 cert_verification_error_ = OK; |
| 51 } |
| 52 |
| 53 virtual void Start() { |
| 54 } |
| 55 |
| 56 virtual int WaitForDataReady(CompletionCallback*) { return OK; } |
| 57 |
| 58 virtual void Persist() { |
| 59 saved_ = Serialize(); |
| 60 } |
| 61 |
| 62 static void Reset() { |
| 63 saved_.clear(); |
| 64 } |
| 65 |
| 66 private: |
| 67 static SSLConfig kDefaultSSLConfig; |
| 68 static std::string saved_; |
| 69 }; |
| 70 |
| 71 std::string TestSSLHostInfo::saved_; |
| 72 |
| 73 SSLConfig TestSSLHostInfo::kDefaultSSLConfig; |
| 74 |
| 75 class SSLClientSocketTLSSRPTest : public PlatformTest { |
| 76 public: |
| 77 SSLClientSocketTLSSRPTest() |
| 78 : child_(base::kNullProcessHandle), |
| 79 socket_factory_(ClientSocketFactory::GetDefaultFactory()), |
| 80 log_(CapturingNetLog::kUnbounded), |
| 81 expected_exit_code_(0) |
| 82 { |
| 83 TestSSLHostInfo::Reset(); |
| 84 } |
| 85 |
| 86 virtual void TearDown() { |
| 87 if (child_ != base::kNullProcessHandle) { |
| 88 int exit_code; |
| 89 EXPECT_TRUE(base::WaitForExitCode(child_, &exit_code)); |
| 90 EXPECT_EQ(expected_exit_code_, exit_code); |
| 91 } |
| 92 } |
| 93 |
| 94 protected: |
| 95 void StartTLSSRPServer(const char* arg, ...) { |
| 96 FilePath dir_exe; |
| 97 PathService::Get(base::DIR_EXE, &dir_exe); |
| 98 FilePath helper_binary = dir_exe.Append("openssl_helper"); |
| 99 |
| 100 std::vector<std::string> args; |
| 101 args.push_back(helper_binary.value()); |
| 102 |
| 103 args.push_back("tls-srp"); |
| 104 args.push_back("--srpv-file"); |
| 105 args.push_back(GetDefaultSRPVFilePath()); |
| 106 |
| 107 // Additional arguments can override the --srpv-file set above. |
| 108 va_list ap; |
| 109 va_start(ap, arg); |
| 110 while (arg) { |
| 111 args.push_back(arg); |
| 112 arg = va_arg(ap, const char*); |
| 113 } |
| 114 va_end(ap); |
| 115 |
| 116 const int listener = socket(AF_INET, SOCK_STREAM, 0); |
| 117 ASSERT_GE(listener, 0); |
| 118 struct sockaddr_in sin; |
| 119 memset(&sin, 0, sizeof(sin)); |
| 120 sin.sin_family = PF_INET; |
| 121 ASSERT_EQ(0, bind(listener, (struct sockaddr*) &sin, sizeof(sin))); |
| 122 socklen_t socklen = sizeof(remote_); |
| 123 ASSERT_EQ(0, getsockname(listener, (struct sockaddr*) &remote_, &socklen)); |
| 124 ASSERT_EQ(sizeof(remote_), socklen); |
| 125 ASSERT_EQ(0, listen(listener, 1)); |
| 126 |
| 127 base::file_handle_mapping_vector mapping; |
| 128 // The listening socket is installed as the child's fd 3. |
| 129 mapping.push_back(std::make_pair(listener, 3)); |
| 130 base::LaunchApp(args, mapping, false /* don't wait */, &child_); |
| 131 ASSERT_EQ(0, HANDLE_EINTR(close(listener))); |
| 132 } |
| 133 |
| 134 // GetDefaultSRPVFilePath returns the path to the default test SRPV file, |
| 135 // ok.srpv |
| 136 std::string GetDefaultSRPVFilePath() { |
| 137 FilePath path; |
| 138 PathService::Get(base::DIR_SOURCE_ROOT, &path); |
| 139 path = path.Append("net"); |
| 140 path = path.Append("data"); |
| 141 path = path.Append("ssl"); |
| 142 path = path.Append("certificates"); |
| 143 path = path.Append("ok.srpv"); |
| 144 return path.value(); |
| 145 } |
| 146 |
| 147 // LoadDefaultCert returns the DER encoded default certificate. |
| 148 std::string LoadDefaultCert() { |
| 149 FilePath path; |
| 150 PathService::Get(base::DIR_SOURCE_ROOT, &path); |
| 151 path = path.Append("net"); |
| 152 path = path.Append("data"); |
| 153 path = path.Append("ssl"); |
| 154 path = path.Append("certificates"); |
| 155 path = path.Append("ok_cert.pem"); |
| 156 |
| 157 std::string pem; |
| 158 bool r = file_util::ReadFileToString(path, &pem); |
| 159 CHECK(r) << "failed to read " << path.value(); |
| 160 |
| 161 static const char kStartMarker[] = "-----BEGIN CERTIFICATE-----\n"; |
| 162 static const char kEndMarker[] = "-----END CERTIFICATE-----\n"; |
| 163 |
| 164 std::string::size_type i = pem.find(kStartMarker); |
| 165 std::string::size_type j = pem.find(kEndMarker); |
| 166 CHECK(i != std::string::npos); |
| 167 CHECK(j != std::string::npos); |
| 168 CHECK_GT(j, i); |
| 169 i += sizeof(kStartMarker) - 1; |
| 170 |
| 171 std::string b64data = pem.substr(i, j - i); |
| 172 ReplaceSubstringsAfterOffset(&b64data, 0 /* start offset */, "\n", ""); |
| 173 |
| 174 std::string der; |
| 175 base::Base64Decode(b64data, &der); |
| 176 return der; |
| 177 } |
| 178 |
| 179 void SetupSSLConfig() { |
| 180 if (ssl_config_.allowed_bad_certs.size()) |
| 181 return; |
| 182 const std::string der = LoadDefaultCert(); |
| 183 SSLConfig::CertAndStatus cert_and_status; |
| 184 cert_and_status.cert = |
| 185 X509Certificate::CreateFromBytes(der.data(), der.size()); |
| 186 cert_and_status.cert_status = ERR_CERT_AUTHORITY_INVALID; |
| 187 |
| 188 ssl_config_.allowed_bad_certs.push_back(cert_and_status); |
| 189 } |
| 190 |
| 191 // PerformConnection makes an SSL connection to the openssl_helper binary and |
| 192 // does a ping-pong test to check the the SSL socket is working correctly. |
| 193 void PerformConnection(std::string username = "", |
| 194 std::string password = "", |
| 195 bool fail = false, |
| 196 int expected_net_error = OK) { |
| 197 client_ = socket(AF_INET, SOCK_STREAM, 0); |
| 198 ASSERT_LE(0, client_); |
| 199 ASSERT_EQ( |
| 200 0, connect(client_, (struct sockaddr*) &remote_, sizeof(remote_))); |
| 201 int on = 1; |
| 202 setsockopt(client_, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)); |
| 203 |
| 204 SetupSSLConfig(); |
| 205 log_.Clear(); |
| 206 |
| 207 std::vector<uint8> localhost; |
| 208 localhost.push_back(127); |
| 209 localhost.push_back(0); |
| 210 localhost.push_back(0); |
| 211 localhost.push_back(1); |
| 212 AddressList addr_list(localhost, 443, false); |
| 213 TCPClientSocket* transport = new TCPClientSocket( |
| 214 addr_list, &log_, NetLog::Source()); |
| 215 |
| 216 if (!username.empty()) { |
| 217 ssl_config_.use_tls_auth = true; |
| 218 ssl_config_.tls_username = username; |
| 219 ssl_config_.tls_password = password; |
| 220 ssl_config_.ssl3_enabled = false; |
| 221 ssl_config_.tls1_enabled = true; |
| 222 } |
| 223 |
| 224 transport->AdoptSocket(client_); |
| 225 scoped_ptr<SSLClientSocket> sock( |
| 226 socket_factory_->CreateSSLClientSocket( |
| 227 transport, HostPortPair("example.com", 443), ssl_config_, |
| 228 new TestSSLHostInfo(&cert_verifier_), &cert_verifier_)); |
| 229 |
| 230 TestCompletionCallback callback; |
| 231 int rv = sock->Connect(&callback); |
| 232 if (rv != OK) { |
| 233 ASSERT_EQ(ERR_IO_PENDING, rv); |
| 234 EXPECT_FALSE(sock->IsConnected()); |
| 235 rv = callback.WaitForResult(); |
| 236 EXPECT_EQ(expected_net_error, rv); // default expected_net_error is OK |
| 237 return; |
| 238 } |
| 239 EXPECT_TRUE(sock->IsConnected()); |
| 240 |
| 241 static const char request_text[] = "whoami"; |
| 242 scoped_refptr<IOBuffer> request_buffer = |
| 243 new IOBuffer(arraysize(request_text) - 1); |
| 244 memcpy(request_buffer->data(), request_text, arraysize(request_text) - 1); |
| 245 rv = sock->Write(request_buffer, arraysize(request_text), &callback); |
| 246 if (rv < 0) { |
| 247 ASSERT_EQ(ERR_IO_PENDING, rv); |
| 248 rv = callback.WaitForResult(); |
| 249 } |
| 250 EXPECT_EQ(7, rv); |
| 251 |
| 252 scoped_refptr<IOBuffer> reply_buffer = new IOBuffer(8); |
| 253 rv = sock->Read(reply_buffer, 8, &callback); |
| 254 if (rv < 0) { |
| 255 ASSERT_EQ(ERR_IO_PENDING, rv); |
| 256 rv = callback.WaitForResult(); |
| 257 } |
| 258 |
| 259 std::string exp_response = (ssl_config_.use_tls_auth) ? |
| 260 ssl_config_.tls_username : "not-using-tls-srp"; |
| 261 |
| 262 EXPECT_EQ((int)exp_response.size(), rv); |
| 263 EXPECT_TRUE(memcmp(reply_buffer->data(), exp_response.data(), |
| 264 exp_response.size()) == 0); |
| 265 |
| 266 next_proto_status_ = sock->GetNextProto(&next_proto_); |
| 267 |
| 268 sock->Disconnect(); |
| 269 } |
| 270 |
| 271 base::ProcessHandle child_; |
| 272 CertVerifier cert_verifier_; |
| 273 ClientSocketFactory* const socket_factory_; |
| 274 struct sockaddr_in remote_; |
| 275 int client_; |
| 276 SSLConfig ssl_config_; |
| 277 CapturingNetLog log_; |
| 278 SSLClientSocket::NextProtoStatus next_proto_status_; |
| 279 std::string next_proto_; |
| 280 int expected_exit_code_; |
| 281 }; |
| 282 |
| 283 TEST_F(SSLClientSocketTLSSRPTest, NoCredentials) { |
| 284 StartTLSSRPServer(NULL); |
| 285 // Not a TLS-SRP connection. |
| 286 PerformConnection("", "", true, ERR_SSL_UNKNOWN_PSK_IDENTITY_ALERT); |
| 287 expected_exit_code_ = 1; |
| 288 } |
| 289 |
| 290 TEST_F(SSLClientSocketTLSSRPTest, GoodCredentials) { |
| 291 StartTLSSRPServer(NULL); |
| 292 PerformConnection("jsmith", "asdf"); |
| 293 } |
| 294 |
| 295 TEST_F(SSLClientSocketTLSSRPTest, GoodCredentials2) { |
| 296 StartTLSSRPServer(NULL); |
| 297 PerformConnection("alice", "1234"); |
| 298 } |
| 299 |
| 300 // TODO(sqs): The current openssl_helper terminates the connection upon seeing |
| 301 // a username it doesn't know. It should instead either (1) make up (s,v) and |
| 302 // play along, or (2) send unknown_psk_identity. When it's fixed, the net error |
| 303 // below will need to change. |
| 304 TEST_F(SSLClientSocketTLSSRPTest, BadCredentials) { |
| 305 StartTLSSRPServer(NULL); |
| 306 PerformConnection("baduser", "badpw", true, |
| 307 ERR_SSL_UNKNOWN_PSK_IDENTITY_ALERT); |
| 308 expected_exit_code_ = 1; |
| 309 } |
| 310 |
| 311 TEST_F(SSLClientSocketTLSSRPTest, GoodUsernameBadPassword) { |
| 312 StartTLSSRPServer(NULL); |
| 313 PerformConnection("jsmith", "badpw", true, ERR_SSL_BAD_RECORD_MAC_ALERT); |
| 314 expected_exit_code_ = 1; |
| 315 } |
| 316 |
| 317 } // namespace net |
OLD | NEW |