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 "build/build_config.h" | |
Mike Belshe
2010/11/08 16:41:10
q: is this intentional?
| |
6 | |
7 #include <arpa/inet.h> | |
8 #include <netinet/tcp.h> | |
9 #include <stdarg.h> | |
10 #include <sys/socket.h> | |
11 #include <sys/types.h> | |
12 | |
13 #include <sslt.h> | |
14 | |
15 #include <vector> | |
16 #include <string> | |
17 | |
18 #include "base/eintr_wrapper.h" | |
19 #include "base/base64.h" | |
20 #include "base/file_path.h" | |
21 #include "base/file_util.h" | |
22 #include "base/path_service.h" | |
23 #include "base/process_util.h" | |
24 #include "base/string_util.h" | |
25 #include "base/values.h" | |
26 #include "net/base/io_buffer.h" | |
27 #include "net/base/net_errors.h" | |
28 #include "net/base/net_log.h" | |
29 #include "net/base/net_log_unittest.h" | |
30 #include "net/base/ssl_config_service.h" | |
31 #include "net/base/test_completion_callback.h" | |
32 #include "net/socket/client_socket_factory.h" | |
33 #include "net/socket/ssl_client_socket.h" | |
34 #include "net/socket/ssl_client_socket_nss.h" | |
35 #include "net/socket/ssl_host_info.h" | |
36 #include "net/socket/tcp_client_socket.h" | |
37 #include "testing/gtest/include/gtest/gtest.h" | |
38 #include "testing/platform_test.h" | |
39 | |
40 namespace net { | |
41 | |
42 // TestSSLHostInfo is an SSLHostInfo which stores a single state in memory and | |
43 // pretends that certificate verification always succeeds. | |
44 class TestSSLHostInfo : public SSLHostInfo { | |
45 public: | |
46 TestSSLHostInfo() | |
47 : SSLHostInfo("example.com", kDefaultSSLConfig) { | |
48 if (!saved_.empty()) | |
49 Parse(saved_); | |
50 cert_verification_complete_ = true; | |
51 cert_verification_result_ = OK; | |
52 } | |
53 | |
54 virtual void Start() { | |
55 NOTREACHED(); | |
Paweł Hajdan Jr.
2010/11/08 16:55:33
Please read http://www.chromium.org/developers/cod
| |
56 } | |
57 | |
58 virtual int WaitForDataReady(CompletionCallback*) { return OK; } | |
59 | |
60 virtual void Persist() { | |
61 saved_ = Serialize(); | |
62 } | |
63 | |
64 static void Reset() { | |
65 saved_.clear(); | |
66 } | |
67 | |
68 private: | |
69 static SSLConfig kDefaultSSLConfig; | |
70 static std::string saved_; | |
71 }; | |
72 | |
73 std::string TestSSLHostInfo::saved_; | |
74 | |
75 SSLConfig TestSSLHostInfo::kDefaultSSLConfig; | |
76 | |
77 class SSLClientSocketSnapStartTest : public PlatformTest { | |
78 public: | |
79 SSLClientSocketSnapStartTest() | |
80 : child_(base::kNullProcessHandle), | |
81 socket_factory_(ClientSocketFactory::GetDefaultFactory()), | |
82 log_(CapturingNetLog::kUnbounded) { | |
83 TestSSLHostInfo::Reset(); | |
84 ssl_config_.snap_start_enabled = true; | |
85 } | |
86 | |
87 virtual void TearDown() { | |
88 if (child_ != base::kNullProcessHandle) { | |
89 int exit_code; | |
90 EXPECT_TRUE(base::WaitForExitCode(child_, &exit_code)); | |
91 EXPECT_EQ(0, exit_code); | |
92 } | |
93 } | |
94 | |
95 protected: | |
96 void StartSnapStartServer(const char* arg, ...) { | |
97 FilePath dir_exe; | |
98 if (!PathService::Get(base::DIR_EXE, &dir_exe)) | |
99 LOG(FATAL) << "Failed to get DIR_EXE"; | |
Paweł Hajdan Jr.
2010/11/08 16:55:33
Same here, and all other similar places.
| |
100 FilePath helper_binary = dir_exe.Append("openssl_helper"); | |
101 | |
102 std::vector<std::string> args; | |
103 args.push_back(helper_binary.value()); | |
104 | |
105 va_list ap; | |
106 va_start(ap, arg); | |
107 while (arg) { | |
108 args.push_back(arg); | |
109 arg = va_arg(ap, const char*); | |
110 } | |
111 va_end(ap); | |
112 | |
113 const int listener = socket(AF_INET, SOCK_STREAM, 0); | |
114 ASSERT_GE(listener, 0); | |
115 struct sockaddr_in sin; | |
116 memset(&sin, 0, sizeof(sin)); | |
117 sin.sin_family = PF_INET; | |
118 ASSERT_EQ(0, bind(listener, (struct sockaddr*) &sin, sizeof(sin))); | |
119 socklen_t socklen = sizeof(remote_); | |
120 ASSERT_EQ(0, getsockname(listener, (struct sockaddr*) &remote_, &socklen)); | |
121 ASSERT_EQ(sizeof(remote_), socklen); | |
122 ASSERT_EQ(0, listen(listener, 1)); | |
123 | |
124 base::file_handle_mapping_vector mapping; | |
125 // The listening socket is installed as the child's fd 3. | |
126 mapping.push_back(std::make_pair(listener, 3)); | |
127 base::LaunchApp(args, mapping, false /* don't wait */, &child_); | |
128 HANDLE_EINTR(close(listener)); | |
129 } | |
130 | |
131 // LoadDefaultCert returns the DER encoded default certificate. | |
132 std::string LoadDefaultCert() { | |
133 FilePath path; | |
134 if (!PathService::Get(base::DIR_SOURCE_ROOT, &path)) | |
135 LOG(FATAL) << "Failed to get DIR_SOURCE_ROOT"; | |
136 path = path.Append("net"); | |
137 path = path.Append("data"); | |
138 path = path.Append("ssl"); | |
139 path = path.Append("certificates"); | |
140 path = path.Append("ok_cert.pem"); | |
141 | |
142 std::string pem; | |
143 bool r = file_util::ReadFileToString(path, &pem); | |
144 CHECK(r) << "failed to read " << path.value(); | |
Paweł Hajdan Jr.
2010/11/08 16:55:33
CHECK is bad too.
| |
145 | |
146 static const char kStartMarker[] = "-----BEGIN CERTIFICATE-----\n"; | |
147 static const char kEndMarker[] = "-----END CERTIFICATE-----\n"; | |
148 | |
149 std::string::size_type i = pem.find(kStartMarker); | |
150 std::string::size_type j = pem.find(kEndMarker); | |
151 CHECK(i != std::string::npos); | |
152 CHECK(j != std::string::npos); | |
153 CHECK_GT(j, i); | |
154 i += sizeof(kStartMarker) - 1; | |
155 | |
156 std::string b64data = pem.substr(i, j - i); | |
157 ReplaceSubstringsAfterOffset(&b64data, 0 /* start offset */, "\n", ""); | |
158 | |
159 std::string der; | |
160 base::Base64Decode(b64data, &der); | |
161 return der; | |
162 } | |
163 | |
164 void SetupSSLConfig() { | |
165 if (ssl_config_.allowed_bad_certs.size()) | |
166 return; | |
167 const std::string der = LoadDefaultCert(); | |
168 SSLConfig::CertAndStatus cert_and_status; | |
169 cert_and_status.cert = | |
170 X509Certificate::CreateFromBytes(der.data(), der.size()); | |
171 cert_and_status.cert_status = ERR_CERT_AUTHORITY_INVALID; | |
172 | |
173 ssl_config_.allowed_bad_certs.push_back(cert_and_status); | |
174 } | |
175 | |
176 // PerformConnection makes an SSL connection to the openssl_helper binary and | |
177 // does a ping-pong test to check the the SSL socket is working correctly. | |
178 void PerformConnection() { | |
179 client_ = socket(AF_INET, SOCK_STREAM, 0); | |
180 ASSERT_LE(0, client_); | |
181 ASSERT_EQ( | |
182 0, connect(client_, (struct sockaddr*) &remote_, sizeof(remote_))); | |
183 int on = 1; | |
184 setsockopt(client_, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)); | |
185 | |
186 SetupSSLConfig(); | |
187 log_.Clear(); | |
188 | |
189 std::vector<uint8> localhost; | |
190 localhost.push_back(127); | |
191 localhost.push_back(0); | |
192 localhost.push_back(0); | |
193 localhost.push_back(1); | |
194 AddressList addr_list(localhost, 443, false); | |
195 TCPClientSocket* transport = new TCPClientSocket( | |
196 addr_list, &log_, NetLog::Source()); | |
197 | |
198 transport->AdoptSocket(client_); | |
199 scoped_ptr<SSLClientSocket> sock( | |
200 socket_factory_->CreateSSLClientSocket(transport, | |
201 "example.com", ssl_config_, new TestSSLHostInfo())); | |
202 | |
203 TestCompletionCallback callback; | |
204 int rv = sock->Connect(&callback); | |
205 if (rv != OK) { | |
206 ASSERT_EQ(ERR_IO_PENDING, rv); | |
207 EXPECT_FALSE(sock->IsConnected()); | |
208 rv = callback.WaitForResult(); | |
209 EXPECT_EQ(OK, rv); | |
210 } | |
211 EXPECT_TRUE(sock->IsConnected()); | |
212 | |
213 static const char request_text[] = "hello!"; | |
214 scoped_refptr<IOBuffer> request_buffer = | |
215 new IOBuffer(arraysize(request_text) - 1); | |
216 memcpy(request_buffer->data(), request_text, arraysize(request_text) - 1); | |
217 rv = sock->Write(request_buffer, arraysize(request_text), &callback); | |
218 if (rv < 0) { | |
219 ASSERT_EQ(ERR_IO_PENDING, rv); | |
220 rv = callback.WaitForResult(); | |
221 } | |
222 EXPECT_EQ(7, rv); | |
223 | |
224 scoped_refptr<IOBuffer> reply_buffer = new IOBuffer(8); | |
225 rv = sock->Read(reply_buffer, 8, &callback); | |
226 if (rv < 0) { | |
227 ASSERT_EQ(ERR_IO_PENDING, rv); | |
228 rv = callback.WaitForResult(); | |
229 } | |
230 EXPECT_EQ(8, rv); | |
231 EXPECT_TRUE(memcmp(reply_buffer->data(), "goodbye!", 8) == 0); | |
232 | |
233 sock->Disconnect(); | |
234 } | |
235 | |
236 // SnapStartEventType extracts the type of Snap Start from the NetLog. See | |
237 // the SSL_SNAP_START_* defines in sslt.h | |
238 int SnapStartEventType() { | |
239 const std::vector<CapturingNetLog::Entry>& entries = log_.entries(); | |
240 for (std::vector<CapturingNetLog::Entry>::const_iterator | |
241 i = entries.begin(); i != entries.end(); i++) { | |
242 if (i->type == NetLog::TYPE_SSL_SNAP_START) { | |
243 scoped_ptr<Value> value(i->extra_parameters->ToValue()); | |
244 CHECK(value->GetType() == Value::TYPE_DICTIONARY); | |
245 DictionaryValue* dict = reinterpret_cast<DictionaryValue*>(value.get()); | |
246 int ret; | |
247 CHECK(dict->GetInteger("type", &ret)); | |
248 return ret; | |
249 } | |
250 } | |
251 | |
252 NOTREACHED(); | |
253 return -1; | |
254 } | |
255 | |
256 // DidMerge returns true if the NetLog suggests the the SSL connection | |
257 // merge it's certificate validation with the optimistic validation from | |
Mike Belshe
2010/11/08 16:41:10
nit: /merge/merged
| |
258 // the SSLHostInfo. | |
259 bool DidMerge() { | |
260 const std::vector<CapturingNetLog::Entry>& entries = log_.entries(); | |
261 for (std::vector<CapturingNetLog::Entry>::const_iterator | |
262 i = entries.begin(); i != entries.end(); i++) { | |
263 if (i->type == NetLog::TYPE_SSL_VERIFICATION_MERGED) | |
264 return true; | |
265 } | |
266 return false; | |
267 } | |
268 | |
269 base::ProcessHandle child_; | |
270 ClientSocketFactory* const socket_factory_; | |
271 struct sockaddr_in remote_; | |
272 int client_; | |
273 SSLConfig ssl_config_; | |
274 CapturingNetLog log_; | |
275 }; | |
276 | |
277 TEST_F(SSLClientSocketSnapStartTest, Basic) { | |
278 // Not a Snap Start connection. | |
279 StartSnapStartServer(NULL); | |
280 PerformConnection(); | |
281 EXPECT_EQ(SSL_SNAP_START_NONE, SnapStartEventType()); | |
282 EXPECT_FALSE(DidMerge()); | |
283 } | |
284 | |
285 TEST_F(SSLClientSocketSnapStartTest, SnapStart) { | |
286 StartSnapStartServer("snap-start", NULL); | |
287 PerformConnection(); | |
288 EXPECT_EQ(SSL_SNAP_START_NONE, SnapStartEventType()); | |
289 EXPECT_FALSE(DidMerge()); | |
290 SSLClientSocketNSS::DropSessionCache(); | |
291 PerformConnection(); | |
292 EXPECT_EQ(SSL_SNAP_START_FULL, SnapStartEventType()); | |
293 EXPECT_TRUE(DidMerge()); | |
294 } | |
295 | |
296 TEST_F(SSLClientSocketSnapStartTest, SnapStartResume) { | |
297 StartSnapStartServer("snap-start", NULL); | |
298 PerformConnection(); | |
299 EXPECT_EQ(SSL_SNAP_START_NONE, SnapStartEventType()); | |
300 EXPECT_FALSE(DidMerge()); | |
301 PerformConnection(); | |
302 EXPECT_EQ(SSL_SNAP_START_RESUME, SnapStartEventType()); | |
303 EXPECT_TRUE(DidMerge()); | |
304 } | |
305 | |
306 TEST_F(SSLClientSocketSnapStartTest, SnapStartRecovery) { | |
307 StartSnapStartServer("snap-start-recovery", NULL); | |
308 PerformConnection(); | |
309 EXPECT_EQ(SSL_SNAP_START_NONE, SnapStartEventType()); | |
310 EXPECT_FALSE(DidMerge()); | |
311 SSLClientSocketNSS::DropSessionCache(); | |
312 PerformConnection(); | |
313 EXPECT_EQ(SSL_SNAP_START_RECOVERY, SnapStartEventType()); | |
314 EXPECT_TRUE(DidMerge()); | |
315 } | |
316 | |
317 TEST_F(SSLClientSocketSnapStartTest, SnapStartResumeRecovery) { | |
318 StartSnapStartServer("snap-start-recovery", NULL); | |
319 PerformConnection(); | |
320 EXPECT_EQ(SSL_SNAP_START_NONE, SnapStartEventType()); | |
321 EXPECT_FALSE(DidMerge()); | |
322 PerformConnection(); | |
323 EXPECT_EQ(SSL_SNAP_START_RESUME_RECOVERY, SnapStartEventType()); | |
324 EXPECT_TRUE(DidMerge()); | |
325 } | |
326 | |
327 } // namespace net | |
OLD | NEW |