OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | |
2 // for details. All rights reserved. Use of this source code is governed by a | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 #include "bin/tls_socket.h" | |
6 | |
7 #include <errno.h> | |
8 #include <fcntl.h> | |
9 #include <sys/stat.h> | |
10 #include <unistd.h> | |
11 #include <libgen.h> | |
12 #include <stdio.h> | |
13 #include <string.h> | |
14 | |
15 #include <prinit.h> | |
16 #include <prerror.h> | |
17 #include <prnetdb.h> | |
18 #include <nss.h> | |
19 #include <ssl.h> | |
20 | |
21 #include "bin/builtin.h" | |
22 #include "bin/dartutils.h" | |
23 #include "bin/thread.h" | |
24 #include "bin/utils.h" | |
25 #include "bin/net/nss_memio.h" | |
26 #include "platform/utils.h" | |
27 | |
28 #include "include/dart_api.h" | |
29 | |
30 bool TlsFilter::library_initialized_ = false; | |
31 | |
32 static TlsFilter* NativePeer(Dart_NativeArguments args) { | |
Mads Ager (google)
2012/09/28 12:03:56
Maybe call this GetTlsFiler?
Bill Hesse
2012/10/31 16:33:29
Done.
| |
33 TlsFilter* native_peer; | |
34 | |
35 Dart_Handle dart_this = HandleError(Dart_GetNativeArgument(args, 0)); | |
36 ASSERT(Dart_IsInstance(dart_this)); | |
37 HandleError(Dart_GetNativeInstanceField(dart_this, 0, | |
Mads Ager (google)
2012/09/28 12:03:56
Introduce a named constant for the 0.
static con
Bill Hesse
2012/10/31 16:33:29
Done.
| |
38 reinterpret_cast<intptr_t*>(&native_peer))); | |
39 return native_peer; | |
40 } | |
41 | |
42 | |
43 static void SetNativePeer(Dart_NativeArguments args, TlsFilter* native_peer) { | |
Mads Ager (google)
2012/09/28 12:03:56
SetTlsFilter?
Bill Hesse
2012/10/31 16:33:29
Done.
| |
44 Dart_Handle dart_this = HandleError(Dart_GetNativeArgument(args, 0)); | |
Mads Ager (google)
2012/09/28 12:03:56
Ditto on HandleError.
| |
45 ASSERT(Dart_IsInstance(dart_this)); | |
46 HandleError(Dart_SetNativeInstanceField(dart_this, 0, | |
47 reinterpret_cast<intptr_t>(native_peer))); | |
48 } | |
49 | |
50 | |
51 void FUNCTION_NAME(TlsSocket_Init)(Dart_NativeArguments args) { | |
52 Dart_EnterScope(); | |
53 TlsFilter* native_peer = new TlsFilter; | |
Mads Ager (google)
2012/09/28 12:03:57
native_peer -> filter?
Bill Hesse
2012/10/31 16:33:29
Done.
| |
54 Dart_Handle dart_this = Dart_GetNativeArgument(args, 0); | |
55 if (Dart_IsError(dart_this)) { | |
56 delete native_peer; | |
57 Dart_PropagateError(dart_this); | |
58 } | |
59 SetNativePeer(args, native_peer); | |
60 native_peer->Init(dart_this); | |
61 | |
62 Dart_SetReturnValue(args, Dart_Null()); | |
Mads Ager (google)
2012/09/28 12:03:57
This is the default behavior so you do not need th
Bill Hesse
2012/10/31 16:33:29
Removed everywhere.
On 2012/09/28 12:03:57, Mads
| |
63 Dart_ExitScope(); | |
64 } | |
65 | |
66 | |
67 void FUNCTION_NAME(TlsSocket_Connect)(Dart_NativeArguments args) { | |
68 Dart_EnterScope(); | |
69 NativePeer(args)->Connect(); | |
70 Dart_SetReturnValue(args, Dart_Null()); | |
71 Dart_ExitScope(); | |
72 } | |
73 | |
74 | |
75 void FUNCTION_NAME(TlsSocket_Destroy)(Dart_NativeArguments args) { | |
76 Dart_EnterScope(); | |
77 TlsFilter* native_peer = NativePeer(args); | |
78 SetNativePeer(args, NULL); | |
79 native_peer->Destroy(); | |
80 delete native_peer; | |
81 Dart_SetReturnValue(args, Dart_Null()); | |
82 Dart_ExitScope(); | |
83 } | |
84 | |
85 | |
86 void FUNCTION_NAME(TlsSocket_RegisterHandshakeCallbacks)( | |
87 Dart_NativeArguments args) { | |
88 Dart_EnterScope(); | |
89 Dart_Handle handshake_start = HandleError(Dart_GetNativeArgument(args, 1)); | |
90 Dart_Handle handshake_finish = HandleError(Dart_GetNativeArgument(args, 2)); | |
91 if (!Dart_IsClosure(handshake_start) || | |
92 !Dart_IsClosure(handshake_finish)) { | |
93 Dart_ThrowException(DartUtils::NewDartIllegalArgumentException( | |
94 "Illegal argument to RegisterHandshakeCallbacks")); | |
95 } | |
96 NativePeer(args)->RegisterHandshakeCallbacks(handshake_start, | |
97 handshake_finish); | |
98 Dart_SetReturnValue(args, Dart_Null()); | |
99 Dart_ExitScope(); | |
100 } | |
101 | |
102 | |
103 void FUNCTION_NAME(TlsSocket_ProcessBuffer)(Dart_NativeArguments args) { | |
104 Dart_EnterScope(); | |
105 Dart_Handle buffer_id_object = HandleError(Dart_GetNativeArgument(args, 1)); | |
106 int64_t buffer_id = DartUtils::GetIntegerValue(buffer_id_object); | |
107 if (buffer_id < 0 || buffer_id >= TlsFilter::kNumBuffers) { | |
108 Dart_ThrowException(DartUtils::NewDartIllegalArgumentException( | |
109 "Illegal argument to ProcessBuffer")); | |
110 } | |
111 | |
112 intptr_t bytes_read = | |
113 NativePeer(args)->ProcessBuffer(static_cast<int>(buffer_id)); | |
114 Dart_SetReturnValue(args, Dart_NewInteger(bytes_read)); | |
115 Dart_ExitScope(); | |
116 } | |
117 | |
118 | |
119 void FUNCTION_NAME(TlsSocket_SetCertificateDatabase) | |
120 (Dart_NativeArguments args) { | |
121 Dart_EnterScope(); | |
122 Dart_Handle dart_pkcert_dir = HandleError(Dart_GetNativeArgument(args, 0)); | |
123 // Check that the type is string, and get the UTF-8 C string value from it. | |
124 if (Dart_IsString(dart_pkcert_dir)) { | |
125 const char* pkcert_dir = NULL; | |
126 HandleError(Dart_StringToCString(dart_pkcert_dir, &pkcert_dir)); | |
127 TlsFilter::InitializeLibrary(pkcert_dir); | |
128 } else { | |
129 Dart_ThrowException(DartUtils::NewDartIllegalArgumentException( | |
130 "Non-String argument to SetCertificateDatabase")); | |
131 } | |
132 Dart_SetReturnValue(args, Dart_Null()); | |
133 Dart_ExitScope(); | |
134 } | |
135 | |
136 void TlsFilter::Init(Dart_Handle dart_this) { | |
137 stringStart_ = HandleError( | |
138 Dart_NewPersistentHandle(Dart_NewString("start"))); | |
139 stringLength_ = HandleError( | |
140 Dart_NewPersistentHandle(Dart_NewString("length"))); | |
141 | |
142 InitializeBuffers(dart_this); | |
143 InitializePlatformData(); | |
144 } | |
145 | |
146 | |
147 void TlsFilter::InitializeBuffers(Dart_Handle dart_this) { | |
148 // Create TlsFilter buffers as ExternalUint8Array objects. | |
149 Dart_Handle dart_buffers_object = HandleError( | |
150 Dart_GetField(dart_this, Dart_NewString("buffers"))); | |
151 Dart_Handle dart_buffer_object = HandleError( | |
152 Dart_ListGetAt(dart_buffers_object, kReadPlaintext)); | |
153 Dart_Handle tlsExternalBuffer_class = HandleError( | |
Mads Ager (google)
2012/09/28 12:03:57
Please use C++ naming convention: tls_external_buf
Bill Hesse
2012/10/31 16:33:29
Done.
| |
154 Dart_InstanceGetClass(dart_buffer_object)); | |
155 Dart_Handle dart_buffer_size = HandleError( | |
156 Dart_GetField(tlsExternalBuffer_class, Dart_NewString("kSize"))); | |
157 buffer_size_ = DartUtils::GetIntegerValue(dart_buffer_size); | |
158 if (buffer_size_ <= 0 || buffer_size_ > 1024 * 1024) { | |
159 Dart_ThrowException( | |
160 Dart_NewString("Invalid buffer size in _TlsExternalBuffer")); | |
161 } | |
162 | |
163 for (int i = 0; i < kNumBuffers; ++i) { | |
164 dart_buffer_objects_[i] = HandleError( | |
165 Dart_NewPersistentHandle(Dart_ListGetAt(dart_buffers_object, i))); | |
166 buffers_[i] = new uint8_t[buffer_size_]; | |
167 Dart_Handle data = HandleError( | |
168 Dart_NewExternalByteArray(buffers_[i], | |
169 buffer_size_, NULL, NULL)); | |
170 HandleError( | |
171 Dart_SetField(dart_buffer_objects_[i], Dart_NewString("data"), data)); | |
172 } | |
173 } | |
174 | |
175 | |
176 void TlsFilter::RegisterHandshakeCallbacks(Dart_Handle start, | |
177 Dart_Handle finish) { | |
178 handshake_start_ = HandleError(Dart_NewPersistentHandle(start)); | |
179 handshake_finish_ = HandleError(Dart_NewPersistentHandle(finish)); | |
180 } | |
181 | |
182 | |
183 void TlsFilter::DestroyPlatformIndependent() { | |
184 for (int i = 0; i < kNumBuffers; ++i) { | |
185 Dart_DeletePersistentHandle(dart_buffer_objects_[i]); | |
186 } | |
187 Dart_DeletePersistentHandle(stringStart_); | |
188 Dart_DeletePersistentHandle(stringLength_); | |
189 } | |
190 | |
191 | |
192 class TlsFilterPlatformData { | |
193 public: | |
194 PRFileDesc* memio; | |
195 }; | |
196 | |
197 | |
198 TlsFilter::TlsFilter() : in_handshake_(false) { } | |
199 | |
200 | |
201 void TlsFilter::InitializeLibrary(const char* pkcert_database) { | |
202 printf("Entering InitializeLibrary\n"); | |
203 LockInitMutex(); | |
204 if (!library_initialized_) { | |
205 PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); | |
206 printf("%s\n", pkcert_database); | |
Mads Ager (google)
2012/09/28 12:03:57
Please remove debug printing.
| |
207 #ifdef CHROMIUM_NSS | |
Søren Gjesse
2012/09/28 09:17:31
We should avoid #ifdefs like this. How about havin
Mads Ager (google)
2012/09/28 12:03:57
I'm not convinced that our build should have to su
Bill Hesse
2012/10/31 16:33:29
All #ifdefs removed, only Chromium NSS supported,
| |
208 // The Chromium version of the NSS libraries does not allow read-only here. | |
209 SECStatus status = | |
210 NSS_InitReadWrite(pkcert_database); | |
211 #else | |
212 SECStatus status = | |
213 NSS_Init(pkcert_database); | |
214 #endif | |
215 if (status == SECSuccess) { | |
216 printf("Successful NSS_Init call\n"); | |
Mads Ager (google)
2012/09/28 12:03:57
Please remove debug printing. You should probably
| |
217 } else { | |
218 PRErrorCode pr_error = PR_GetError(); | |
219 printf("Unsuccessful NSS_Init call. Error %d\n", pr_error); | |
220 } | |
221 | |
222 status = NSS_SetDomesticPolicy(); | |
223 if (status == SECSuccess) { | |
224 printf("Successful NSS_SetDomesticPolicy call\n"); | |
225 } else { | |
226 PRErrorCode pr_error = PR_GetError(); | |
227 printf("Unsuccessful NSS_SetDomesticPolicy call. Error %d\n", pr_error); | |
228 } | |
229 } else { | |
230 printf("Called TlsFilter::InitializeLibrary more than once.\n"); | |
231 } | |
232 UnlockInitMutex(); | |
233 printf("Exiting InitializeLibrary\n"); | |
234 } | |
235 | |
236 | |
237 void TlsFilter::InitializePlatformData() { | |
238 data_ = new TlsFilterPlatformData; | |
239 data_->memio = memio_CreateIOLayer(30000); | |
Mads Ager (google)
2012/09/28 12:03:57
Maybe we can create a named constant for this valu
| |
240 } | |
241 | |
242 | |
243 char host_entry_buffer[PR_NETDB_BUF_SIZE]; | |
244 PRHostEnt host_entry; | |
245 PRNetAddr host_address; | |
246 PRFileDesc* my_socket; | |
247 | |
248 bool SECURITY = true; | |
Mads Ager (google)
2012/09/28 12:03:57
Please remove.
| |
249 SECStatus status; | |
250 | |
251 // Callback that always returns SECSuccess. | |
252 static SECStatus AlwaysSucceed(void* arg, PRFileDesc *fd, | |
253 PRBool check, PRBool server) { | |
254 return SECSuccess; | |
255 } | |
256 | |
257 void TlsFilter::Connect() { | |
258 printf("Entering InitSocket\n"); | |
259 if (!in_handshake_) { | |
260 my_socket = data_->memio; | |
261 | |
262 my_socket = SSL_ImportFD(NULL, my_socket); | |
263 if (my_socket == NULL) { | |
264 printf("SSL_ImportFD failed.\n"); | |
Mads Ager (google)
2012/09/28 12:03:57
Similarly here. We should report this error back t
| |
265 } | |
266 | |
267 if (SSL_SetURL(my_socket, "www.google.dk") == -1) { | |
268 printf("SetURL call failed\n"); | |
269 } | |
270 | |
271 if (!SECURITY) { | |
272 status = SSL_OptionSet(my_socket, SSL_SECURITY, PR_FALSE); | |
273 } | |
274 if (status == SECSuccess) { | |
275 printf("Successful SSL_OptionSetDefault call\n"); | |
276 } else { | |
277 PRErrorCode pr_error = PR_GetError(); | |
278 printf("Unsuccessful SSL_OptionSetDefault call. Error %d\n", pr_error); | |
279 } | |
280 | |
281 #ifdef CHROMIUM_NSS | |
Mads Ager (google)
2012/09/28 12:03:57
Since we always use CHROMIUM_NSS we can get rid of
| |
282 // Certificate chain verification is not yet working with Chromium NSS. | |
283 status = SSL_AuthCertificateHook(my_socket, AlwaysSucceed, NULL); | |
284 if (status == SECSuccess) { | |
285 printf("Successful SSL_AuthCertificateHook call\n"); | |
286 } else { | |
287 PRErrorCode pr_error = PR_GetError(); | |
288 printf("Unsuccessful SSL_AuthCertificateHook call. Error %d\n", pr_error); | |
289 } | |
290 #endif | |
291 | |
292 status = SSL_ResetHandshake(my_socket, PR_FALSE); | |
293 if (status == SECSuccess) { | |
294 printf("Successful SSL_ResetHandshake call\n"); | |
295 } else { | |
296 PRErrorCode pr_error = PR_GetError(); | |
297 printf("Unsuccessful SSL_ResetHandshake call. Error %d\n", pr_error); | |
298 } | |
299 | |
300 // SetPeerAddress | |
301 PRNetAddr host_address; | |
302 char host_entry_buffer[PR_NETDB_BUF_SIZE]; | |
303 PRHostEnt host_entry; | |
304 PRStatus rv = PR_GetHostByName("www.google.dk", host_entry_buffer, | |
305 PR_NETDB_BUF_SIZE, &host_entry); | |
306 if (rv == PR_SUCCESS) { | |
307 printf("Successful PR_GetHostByName call\n"); | |
308 } else { | |
309 PRErrorCode pr_error = PR_GetError(); | |
310 printf("Unsuccessful PR_GetHostByName call. Error %d\n", pr_error); | |
311 } | |
312 | |
313 int index = PR_EnumerateHostEnt(0, &host_entry, 443, &host_address); | |
314 if (index == -1 || index == 0) { | |
315 printf("Unsuccessful PR_EnumerateHostEnt call.\n"); | |
316 } | |
317 if (host_address.inet.family == PR_AF_INET) { | |
318 printf("Got IP V4 address\n"); | |
319 } | |
320 | |
321 memio_SetPeerName(my_socket, &host_address); | |
322 | |
323 data_->memio = my_socket; | |
324 } | |
325 | |
326 // Handshake | |
327 int er; | |
328 if ((er = SSL_ForceHandshake(my_socket)) == SECSuccess) { | |
329 printf("Successful SSL_ForceHandshake call\n"); | |
330 if (in_handshake_) { | |
331 printf("Exiting handshake state\n"); | |
332 HandleError(Dart_InvokeClosure(handshake_finish_, 0, NULL)); | |
333 in_handshake_ = false; | |
334 } | |
335 } else { | |
336 printf("Unsuccessful SSL_ForceHandshake call\n"); | |
337 PRErrorCode pr_error = PR_GetError(); | |
338 printf("Error code %d %d\n", er, pr_error); | |
339 if (!in_handshake_) { | |
340 printf("Entering handshake state\n"); | |
341 // int writable = ProcessBuffer(kWriteEncrypted); | |
342 // printf("ProcessBuffer(kWriteEncrypted) returns %d\n", writable); | |
343 HandleError(Dart_InvokeClosure(handshake_start_, 0, NULL)); | |
344 in_handshake_ = true; | |
345 } | |
346 } | |
347 | |
348 if (!SECURITY) { | |
349 // Handshake doesn't happen. | |
350 HandleError(Dart_InvokeClosure(handshake_start_, 0, NULL)); | |
351 HandleError(Dart_InvokeClosure(handshake_finish_, 0, NULL)); | |
352 } | |
353 } | |
354 | |
355 | |
356 void TlsFilter::Destroy() { | |
357 DestroyPlatformIndependent(); | |
358 // TODO(whesse): Destroy OpenSSL objects here. | |
Mads Ager (google)
2012/09/28 12:03:57
OpenSSL?
| |
359 } | |
360 | |
361 intptr_t TlsFilter::ProcessBuffer(int buffer_index) { | |
362 printf("Entering processBuffer(%d)\n", buffer_index); | |
363 Dart_Handle buffer_object = dart_buffer_objects_[buffer_index]; | |
364 Dart_Handle start_object = HandleError( | |
365 Dart_GetField(buffer_object, stringStart_)); | |
366 Dart_Handle length_object = HandleError( | |
367 Dart_GetField(buffer_object, stringLength_)); | |
368 int64_t unsafe_start = DartUtils::GetIntegerValue(start_object); | |
369 int64_t unsafe_length = DartUtils::GetIntegerValue(length_object); | |
370 if (unsafe_start < 0 || unsafe_start >= buffer_size_ || | |
371 unsafe_length < 0 || unsafe_length > buffer_size_) { | |
372 Dart_ThrowException(DartUtils::NewDartIllegalArgumentException( | |
373 "Illegal .start or .length on a _TlsExternalBuffer")); | |
374 } | |
375 intptr_t start = static_cast<intptr_t>(unsafe_start); | |
376 intptr_t length = static_cast<intptr_t>(unsafe_length); | |
377 | |
378 int bytes_sent = 0; | |
379 switch (buffer_index) { | |
380 case kReadPlaintext: { | |
381 int bytes_free = buffer_size_ - start - length; | |
382 bytes_sent = PR_Read(data_->memio, | |
383 buffers_[buffer_index] + start + length, | |
384 bytes_free); | |
385 printf(" plaintext bytes read %d (into %d)\n", | |
386 bytes_sent, bytes_free); | |
387 if (bytes_sent < 0) bytes_sent = 0; | |
388 break; | |
389 } | |
390 | |
391 case kWriteEncrypted: { | |
392 const uint8_t* buf1; | |
393 const uint8_t* buf2; | |
394 unsigned int len1; | |
395 unsigned int len2; | |
396 int bytes_free = buffer_size_ - start - length; | |
397 memio_Private* secret = memio_GetSecret(data_->memio); | |
398 memio_GetWriteParams(secret, &buf1, &len1, &buf2, &len2); | |
399 int bytes_to_send = | |
400 dart::Utils::Minimum(len1, static_cast<unsigned>(bytes_free)); | |
401 if (bytes_to_send > 0) { | |
402 memmove(buffers_[buffer_index] + start + length, buf1, bytes_to_send); | |
403 bytes_sent = bytes_to_send; | |
404 } | |
405 bytes_to_send = dart::Utils::Minimum(len2, | |
406 static_cast<unsigned>(bytes_free - bytes_sent)); | |
407 if (bytes_to_send > 0) { | |
408 memmove(buffers_[buffer_index] + start + length + bytes_sent, buf2, | |
409 bytes_to_send); | |
410 bytes_sent += bytes_to_send; | |
411 } | |
412 printf(" encrypted bytes written %d (into %d)\n", | |
413 bytes_sent, bytes_free); | |
414 if (bytes_sent > 0) { | |
415 memio_PutWriteResult(secret, bytes_sent); | |
416 } | |
417 break; | |
418 } | |
419 | |
420 case kReadEncrypted: { | |
421 if (length > 0) { | |
422 bytes_sent = length; | |
423 // NEW | |
424 memio_Private* secret = memio_GetSecret(data_->memio); | |
425 uint8_t* memio_buf; | |
426 int free_bytes = memio_GetReadParams(secret, &memio_buf); | |
427 if (free_bytes < bytes_sent) bytes_sent = free_bytes; | |
428 printf(" encrypted bytes read %d (out of %d)\n", | |
429 bytes_sent, static_cast<int>(length)); | |
430 memmove(memio_buf, | |
431 buffers_[buffer_index] + start, | |
432 bytes_sent); | |
433 memio_PutReadResult(secret, bytes_sent); | |
434 } | |
435 break; | |
436 } | |
437 | |
438 case kWritePlaintext: { | |
439 if (length > 0) { | |
440 bytes_sent = PR_Write(data_->memio, | |
441 buffers_[buffer_index] + start, | |
442 length); | |
443 printf(" plaintext bytes written %d out of %d\n", | |
444 static_cast<int>(bytes_sent), static_cast<int>(length)); | |
445 } else { | |
446 printf(" no plaintext bytes to write.\n"); | |
447 } | |
448 if (bytes_sent < 0) bytes_sent = 0; | |
449 break; | |
450 } | |
451 } | |
452 return bytes_sent; | |
453 } | |
OLD | NEW |