| OLD | NEW |
| 1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include <string> | 5 #include <string> |
| 6 #include <algorithm> | 6 #include <algorithm> |
| 7 | 7 |
| 8 #include "net/base/ssl_test_util.h" |
| 9 |
| 8 #include "build/build_config.h" | 10 #include "build/build_config.h" |
| 9 | 11 |
| 10 #if defined(OS_WIN) | 12 #if defined(OS_WIN) |
| 11 #include <windows.h> | 13 #include <windows.h> |
| 12 #include <wincrypt.h> | 14 #include <wincrypt.h> |
| 13 #elif defined(OS_LINUX) | 15 #elif defined(OS_LINUX) |
| 14 #include <nspr.h> | 16 #include <nspr.h> |
| 15 #include <nss.h> | 17 #include <nss.h> |
| 16 #include <secerr.h> | 18 #include <secerr.h> |
| 17 // Work around https://bugzilla.mozilla.org/show_bug.cgi?id=455424 | 19 // Work around https://bugzilla.mozilla.org/show_bug.cgi?id=455424 |
| 18 // until NSS 3.12.2 comes out and we update to it. | 20 // until NSS 3.12.2 comes out and we update to it. |
| 19 #define Lock FOO_NSS_Lock | 21 #define Lock FOO_NSS_Lock |
| 20 #include <ssl.h> | 22 #include <ssl.h> |
| 21 #include <sslerr.h> | 23 #include <sslerr.h> |
| 22 #include <pk11pub.h> | 24 #include <pk11pub.h> |
| 23 #undef Lock | 25 #undef Lock |
| 24 #include "base/nss_init.h" | 26 #include "base/nss_init.h" |
| 25 #endif | 27 #endif |
| 26 | 28 |
| 27 #include "base/file_util.h" | 29 #include "base/file_util.h" |
| 28 #include "base/logging.h" | 30 #include "base/logging.h" |
| 29 #include "base/path_service.h" | 31 #include "base/path_service.h" |
| 32 #include "base/platform_thread.h" |
| 33 #include "base/string_util.h" |
| 34 #include "net/base/tcp_pinger.h" |
| 35 #include "net/base/host_resolver.h" |
| 36 #include "net/base/tcp_client_socket.h" |
| 37 #include "net/base/test_completion_callback.h" |
| 38 #include "testing/platform_test.h" |
| 30 | 39 |
| 31 #include "net/base/ssl_test_util.h" | 40 namespace { |
| 32 | |
| 33 // static | |
| 34 const char SSLTestUtil::kHostName[] = "127.0.0.1"; | |
| 35 const int SSLTestUtil::kOKHTTPSPort = 9443; | |
| 36 const int SSLTestUtil::kBadHTTPSPort = 9666; | |
| 37 | |
| 38 // The issuer name of the cert that should be trusted for the test to work. | |
| 39 const wchar_t SSLTestUtil::kCertIssuerName[] = L"Test CA"; | |
| 40 | 41 |
| 41 #if defined(OS_LINUX) | 42 #if defined(OS_LINUX) |
| 42 static CERTCertificate* LoadTemporaryCert(const FilePath& filename) { | 43 static CERTCertificate* LoadTemporaryCert(const FilePath& filename) { |
| 43 base::EnsureNSSInit(); | 44 base::EnsureNSSInit(); |
| 44 | 45 |
| 45 std::string rawcert; | 46 std::string rawcert; |
| 46 if (!file_util::ReadFileToString(filename.ToWStringHack(), &rawcert)) { | 47 if (!file_util::ReadFileToString(filename.ToWStringHack(), &rawcert)) { |
| 47 LOG(ERROR) << "Can't load certificate " << filename.ToWStringHack(); | 48 LOG(ERROR) << "Can't load certificate " << filename.ToWStringHack(); |
| 48 return NULL; | 49 return NULL; |
| 49 } | 50 } |
| (...skipping 10 matching lines...) Expand all Loading... |
| 60 CERTCertTrust trust; | 61 CERTCertTrust trust; |
| 61 int rv = CERT_DecodeTrustString(&trust, const_cast<char *>("TCu,Cu,Tu")); | 62 int rv = CERT_DecodeTrustString(&trust, const_cast<char *>("TCu,Cu,Tu")); |
| 62 if (rv != SECSuccess) { | 63 if (rv != SECSuccess) { |
| 63 LOG(ERROR) << "Can't decode trust string"; | 64 LOG(ERROR) << "Can't decode trust string"; |
| 64 CERT_DestroyCertificate(cert); | 65 CERT_DestroyCertificate(cert); |
| 65 return NULL; | 66 return NULL; |
| 66 } | 67 } |
| 67 | 68 |
| 68 rv = CERT_ChangeCertTrust(CERT_GetDefaultCertDB(), cert, &trust); | 69 rv = CERT_ChangeCertTrust(CERT_GetDefaultCertDB(), cert, &trust); |
| 69 if (rv != SECSuccess) { | 70 if (rv != SECSuccess) { |
| 70 LOG(ERROR) << "Can't change trust for certificate " | 71 LOG(ERROR) << "Can't change trust for certificate " |
| 71 << filename.ToWStringHack(); | 72 << filename.ToWStringHack(); |
| 72 CERT_DestroyCertificate(cert); | 73 CERT_DestroyCertificate(cert); |
| 73 return NULL; | 74 return NULL; |
| 74 } | 75 } |
| 75 | 76 |
| 76 LOG(INFO) << "Loaded temporary certificate " << filename.ToWStringHack(); | 77 // TODO(dkegel): figure out how to get this to only happen once? |
| 77 return cert; | 78 return cert; |
| 78 } | 79 } |
| 79 #endif | 80 #endif |
| 80 | 81 |
| 81 SSLTestUtil::SSLTestUtil() { | 82 } // namespace |
| 82 PathService::Get(base::DIR_SOURCE_ROOT, &cert_dir_); | 83 |
| 83 cert_dir_ = cert_dir_.AppendASCII("net"); | 84 namespace net { |
| 84 cert_dir_ = cert_dir_.AppendASCII("data"); | 85 |
| 85 cert_dir_ = cert_dir_.AppendASCII("ssl"); | 86 // static |
| 86 cert_dir_ = cert_dir_.AppendASCII("certificates"); | 87 const char TestServerLauncher::kHostName[] = "127.0.0.1"; |
| 87 | 88 const char TestServerLauncher::kMismatchedHostName[] = "localhost"; |
| 89 const int TestServerLauncher::kOKHTTPSPort = 9443; |
| 90 const int TestServerLauncher::kBadHTTPSPort = 9666; |
| 91 |
| 92 // The issuer name of the cert that should be trusted for the test to work. |
| 93 const wchar_t TestServerLauncher::kCertIssuerName[] = L"Test CA"; |
| 94 |
| 95 TestServerLauncher::TestServerLauncher() : process_handle_(NULL) |
| 88 #if defined(OS_LINUX) | 96 #if defined(OS_LINUX) |
| 89 cert_ = reinterpret_cast<PrivateCERTCertificate*>( | 97 , cert_(NULL) |
| 98 #endif |
| 99 { |
| 100 PathService::Get(base::DIR_SOURCE_ROOT, &data_dir_); |
| 101 data_dir_ = data_dir_.Append(FILE_PATH_LITERAL("net")) |
| 102 .Append(FILE_PATH_LITERAL("data")) |
| 103 .Append(FILE_PATH_LITERAL("ssl")); |
| 104 cert_dir_ = data_dir_.Append(FILE_PATH_LITERAL("certificates")); |
| 105 } |
| 106 |
| 107 namespace { |
| 108 |
| 109 void AppendToPythonPath(FilePath dir) { |
| 110 // Do nothing if dir already on path. |
| 111 |
| 112 #if defined(OS_WIN) |
| 113 const wchar_t kPythonPath[] = L"PYTHONPATH"; |
| 114 // FIXME(dkegel): handle longer PYTHONPATH variables |
| 115 wchar_t oldpath[4096]; |
| 116 if (GetEnvironmentVariable(kPythonPath, oldpath, sizeof(oldpath)) == 0) { |
| 117 SetEnvironmentVariableW(kPythonPath, dir.value().c_str()); |
| 118 } else if (!wcsstr(oldpath, dir.value().c_str())) { |
| 119 std::wstring newpath(oldpath); |
| 120 newpath.append(L":"); |
| 121 newpath.append(dir.value()); |
| 122 SetEnvironmentVariableW(kPythonPath, newpath.c_str()); |
| 123 } |
| 124 #elif defined(OS_POSIX) |
| 125 const char kPythonPath[] = "PYTHONPATH"; |
| 126 const char* oldpath = getenv(kPythonPath); |
| 127 if (!oldpath) { |
| 128 setenv(kPythonPath, dir.value().c_str(), 1); |
| 129 } else if (!strstr(oldpath, dir.value().c_str())) { |
| 130 std::string newpath(oldpath); |
| 131 newpath.append(":"); |
| 132 newpath.append(dir.value()); |
| 133 setenv(kPythonPath, newpath.c_str(), 1); |
| 134 } |
| 135 #endif |
| 136 } |
| 137 |
| 138 } // end namespace |
| 139 |
| 140 void TestServerLauncher::SetPythonPath() { |
| 141 FilePath third_party_dir; |
| 142 ASSERT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &third_party_dir)); |
| 143 third_party_dir = third_party_dir.Append(FILE_PATH_LITERAL("third_party")); |
| 144 |
| 145 AppendToPythonPath(third_party_dir.Append(FILE_PATH_LITERAL("tlslite"))); |
| 146 AppendToPythonPath(third_party_dir.Append(FILE_PATH_LITERAL("pyftpdlib"))); |
| 147 } |
| 148 |
| 149 bool TestServerLauncher::Start(Protocol protocol, |
| 150 const std::string& host_name, int port, |
| 151 const FilePath& document_root, |
| 152 const FilePath& cert_path) { |
| 153 if (!TestServerLauncher::CheckCATrusted()) |
| 154 return false; |
| 155 |
| 156 std::string port_str = IntToString(port); |
| 157 |
| 158 // Get path to python server script |
| 159 FilePath testserver_path; |
| 160 if (!PathService::Get(base::DIR_SOURCE_ROOT, &testserver_path)) |
| 161 return false; |
| 162 testserver_path = testserver_path |
| 163 .Append(FILE_PATH_LITERAL("net")) |
| 164 .Append(FILE_PATH_LITERAL("tools")) |
| 165 .Append(FILE_PATH_LITERAL("testserver")) |
| 166 .Append(FILE_PATH_LITERAL("testserver.py")); |
| 167 |
| 168 FilePath test_data_directory; |
| 169 PathService::Get(base::DIR_SOURCE_ROOT, &test_data_directory); |
| 170 test_data_directory = test_data_directory.Append(document_root); |
| 171 |
| 172 #if defined(OS_LINUX) |
| 173 if (!cert_ && !cert_path.value().empty()) { |
| 174 cert_ = reinterpret_cast<PrivateCERTCertificate*>( |
| 90 LoadTemporaryCert(GetRootCertPath())); | 175 LoadTemporaryCert(GetRootCertPath())); |
| 91 DCHECK(cert_); | 176 DCHECK(cert_); |
| 92 #endif | 177 } |
| 93 } | 178 #endif |
| 94 | 179 |
| 95 SSLTestUtil::~SSLTestUtil() { | 180 SetPythonPath(); |
| 181 |
| 182 #if defined(OS_WIN) |
| 183 // Get path to python interpreter |
| 184 if (!PathService::Get(base::DIR_SOURCE_ROOT, &python_runtime_)) |
| 185 return false; |
| 186 python_runtime_ = python_runtime_ |
| 187 .Append(FILE_PATH_LITERAL("third_party")) |
| 188 .Append(FILE_PATH_LITERAL("python_24")) |
| 189 .Append(FILE_PATH_LITERAL("python.exe")); |
| 190 |
| 191 std::wstring command_line = |
| 192 L"\"" + python_runtime_.ToWStringHack() + L"\" " + |
| 193 L"\"" + testserver_path.ToWStringHack() + |
| 194 L"\" --port=" + UTF8ToWide(port_str) + |
| 195 L" --data-dir=\"" + test_data_directory.ToWStringHack() + L"\""; |
| 196 if (protocol == ProtoFTP) |
| 197 command_line.append(L" -f"); |
| 198 if (!cert_path.value().empty()) { |
| 199 command_line.append(L" --https=\""); |
| 200 command_line.append(cert_path.ToWStringHack()); |
| 201 command_line.append(L"\""); |
| 202 } |
| 203 |
| 204 if (!base::LaunchApp(command_line, false, true, &process_handle_)) { |
| 205 LOG(ERROR) << "Failed to launch " << command_line; |
| 206 return false; |
| 207 } |
| 208 #elif defined(OS_POSIX) |
| 209 std::vector<std::string> command_line; |
| 210 command_line.push_back("python"); |
| 211 command_line.push_back(WideToUTF8(testserver_path.ToWStringHack())); |
| 212 command_line.push_back("--port=" + port_str); |
| 213 command_line.push_back("--data-dir=" + |
| 214 WideToUTF8(test_data_directory.ToWStringHack())); |
| 215 if (protocol == ProtoFTP) |
| 216 command_line.push_back("-f"); |
| 217 if (!cert_path.value().empty()) |
| 218 command_line.push_back("--https=" + WideToUTF8(cert_path.ToWStringHack())); |
| 219 |
| 220 base::file_handle_mapping_vector no_mappings; |
| 221 LOG(INFO) << "Trying to launch " << command_line[0] << " ..."; |
| 222 if (!base::LaunchApp(command_line, no_mappings, false, &process_handle_)) { |
| 223 LOG(ERROR) << "Failed to launch " << command_line[0] << " ..."; |
| 224 return false; |
| 225 } |
| 226 #endif |
| 227 |
| 228 // Let the server start, then verify that it's up. |
| 229 // Our server is Python, and takes about 500ms to start |
| 230 // up the first time, and about 200ms after that. |
| 231 if (!Wait(host_name, port)) { |
| 232 LOG(ERROR) << "Failed to connect to server"; |
| 233 Stop(); |
| 234 return false; |
| 235 } |
| 236 |
| 237 LOG(INFO) << "Started on port " << port_str; |
| 238 return true; |
| 239 } |
| 240 |
| 241 bool TestServerLauncher::Wait(const std::string& host_name, int port) { |
| 242 // Verify that the webserver is actually started. |
| 243 // Otherwise tests can fail if they run faster than Python can start. |
| 244 net::AddressList addr; |
| 245 net::HostResolver resolver; |
| 246 int rv = resolver.Resolve(host_name, port, &addr, NULL); |
| 247 if (rv != net::OK) |
| 248 return false; |
| 249 |
| 250 net::TCPPinger pinger(addr); |
| 251 rv = pinger.Ping(); |
| 252 return rv == net::OK; |
| 253 } |
| 254 |
| 255 void TestServerLauncher::Stop() { |
| 256 if (!process_handle_) |
| 257 return; |
| 258 |
| 259 base::KillProcess(process_handle_, 1, true); |
| 260 |
| 261 #if defined(OS_WIN) |
| 262 CloseHandle(process_handle_); |
| 263 #endif |
| 264 |
| 265 process_handle_ = NULL; |
| 266 LOG(INFO) << "Stopped."; |
| 267 } |
| 268 |
| 269 TestServerLauncher::~TestServerLauncher() { |
| 96 #if defined(OS_LINUX) | 270 #if defined(OS_LINUX) |
| 97 if (cert_) | 271 if (cert_) |
| 98 CERT_DestroyCertificate(reinterpret_cast<CERTCertificate*>(cert_)); | 272 CERT_DestroyCertificate(reinterpret_cast<CERTCertificate*>(cert_)); |
| 99 #endif | 273 #endif |
| 100 } | 274 Stop(); |
| 101 | 275 } |
| 102 FilePath SSLTestUtil::GetRootCertPath() { | 276 |
| 277 FilePath TestServerLauncher::GetRootCertPath() { |
| 103 FilePath path(cert_dir_); | 278 FilePath path(cert_dir_); |
| 104 path = path.AppendASCII("root_ca_cert.crt"); | 279 path = path.AppendASCII("root_ca_cert.crt"); |
| 105 return path; | 280 return path; |
| 106 } | 281 } |
| 107 | 282 |
| 108 FilePath SSLTestUtil::GetOKCertPath() { | 283 FilePath TestServerLauncher::GetOKCertPath() { |
| 109 FilePath path(cert_dir_); | 284 FilePath path(cert_dir_); |
| 110 path = path.AppendASCII("ok_cert.pem"); | 285 path = path.AppendASCII("ok_cert.pem"); |
| 111 return path; | 286 return path; |
| 112 } | 287 } |
| 113 | 288 |
| 114 FilePath SSLTestUtil::GetExpiredCertPath() { | 289 FilePath TestServerLauncher::GetExpiredCertPath() { |
| 115 FilePath path(cert_dir_); | 290 FilePath path(cert_dir_); |
| 116 path = path.AppendASCII("expired_cert.pem"); | 291 path = path.AppendASCII("expired_cert.pem"); |
| 117 return path; | 292 return path; |
| 118 } | 293 } |
| 119 | 294 |
| 120 bool SSLTestUtil::CheckCATrusted() { | 295 bool TestServerLauncher::CheckCATrusted() { |
| 121 // TODO(port): Port either this or LoadTemporaryCert to MacOSX. | 296 // TODO(port): Port either this or LoadTemporaryCert to MacOSX. |
| 122 #if defined(OS_WIN) | 297 #if defined(OS_WIN) |
| 123 HCERTSTORE cert_store = CertOpenSystemStore(NULL, L"ROOT"); | 298 HCERTSTORE cert_store = CertOpenSystemStore(NULL, L"ROOT"); |
| 124 if (!cert_store) { | 299 if (!cert_store) { |
| 125 LOG(ERROR) << " could not open trusted root CA store"; | 300 LOG(ERROR) << " could not open trusted root CA store"; |
| 126 return false; | 301 return false; |
| 127 } | 302 } |
| 128 PCCERT_CONTEXT cert = | 303 PCCERT_CONTEXT cert = |
| 129 CertFindCertificateInStore(cert_store, | 304 CertFindCertificateInStore(cert_store, |
| 130 X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, | 305 X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, |
| 131 0, | 306 0, |
| 132 CERT_FIND_ISSUER_STR, | 307 CERT_FIND_ISSUER_STR, |
| 133 kCertIssuerName, | 308 kCertIssuerName, |
| 134 NULL); | 309 NULL); |
| 135 if (cert) | 310 if (cert) |
| 136 CertFreeCertificateContext(cert); | 311 CertFreeCertificateContext(cert); |
| 137 CertCloseStore(cert_store, 0); | 312 CertCloseStore(cert_store, 0); |
| 138 | 313 |
| 139 if (!cert) { | 314 if (!cert) { |
| 140 LOG(ERROR) << " TEST CONFIGURATION ERROR: you need to import the test ca " | 315 LOG(ERROR) << " TEST CONFIGURATION ERROR: you need to import the test ca " |
| 141 "certificate to your trusted roots for this test to work. " | 316 "certificate to your trusted roots for this test to work. " |
| 142 "For more info visit:\n" | 317 "For more info visit:\n" |
| 143 "http://dev.chromium.org/developers/testing\n"; | 318 "http://dev.chromium.org/developers/testing\n"; |
| 144 return false; | 319 return false; |
| 145 } | 320 } |
| 146 #endif | 321 #endif |
| 147 return true; | 322 return true; |
| 148 } | 323 } |
| 324 |
| 325 } // namespace net |
| 326 |
| OLD | NEW |