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