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/secure_socket.h" | |
6 | |
7 #include <errno.h> | |
8 #include <fcntl.h> | |
9 #include <sys/stat.h> | |
10 #include <stdio.h> | |
11 #include <string.h> | |
12 | |
13 #include <openssl/bio.h> | |
14 #include <openssl/err.h> | |
15 #include <openssl/pkcs12.h> | |
16 #include <openssl/safestack.h> | |
17 #include <openssl/ssl.h> | |
18 #include <openssl/tls1.h> | |
19 #include <openssl/x509.h> | |
20 | |
21 #include "bin/builtin.h" | |
22 #include "bin/dartutils.h" | |
23 #include "bin/lockers.h" | |
24 #include "bin/log.h" | |
25 #include "bin/socket.h" | |
26 #include "bin/thread.h" | |
27 #include "bin/utils.h" | |
28 #include "platform/utils.h" | |
29 | |
30 #include "include/dart_api.h" | |
31 | |
32 // Return the error from the containing function if handle is an error handle. | |
33 #define RETURN_IF_ERROR(handle) \ | |
34 { \ | |
35 Dart_Handle __handle = handle; \ | |
36 if (Dart_IsError((__handle))) { \ | |
37 return __handle; \ | |
38 } \ | |
39 } | |
40 | |
41 namespace dart { | |
42 namespace bin { | |
43 | |
44 bool SSLFilter::library_initialized_ = false; | |
45 // To protect library initialization. | |
46 Mutex* SSLFilter::mutex_ = new Mutex(); | |
47 int SSLFilter::filter_ssl_index; | |
48 | |
49 static const int kSSLFilterNativeFieldIndex = 0; | |
50 static const int kSecurityContextNativeFieldIndex = 0; | |
51 static const int kX509NativeFieldIndex = 0; | |
52 | |
53 static const bool SSL_LOG_STATUS = false; | |
54 static const bool SSL_LOG_DATA = false; | |
55 | |
56 static const int SSL_ERROR_MESSAGE_BUFFER_SIZE = 1000; | |
57 | |
58 | |
59 /* Get the error messages from BoringSSL, and put them in buffer as a | |
60 * null-terminated string. */ | |
61 static void FetchErrorString(char* buffer, int length) { | |
62 buffer[0] = '\0'; | |
63 int error = ERR_get_error(); | |
64 while (error != 0) { | |
65 int used = strlen(buffer); | |
66 int free_length = length - used; | |
67 if (free_length > 16) { | |
68 // Enough room for error code at least. | |
69 if (used > 0) { | |
70 buffer[used] = '\n'; | |
71 buffer[used + 1] = '\0'; | |
72 used++; | |
73 free_length--; | |
74 } | |
75 ERR_error_string_n(error, buffer + used, free_length); | |
76 // ERR_error_string_n is guaranteed to leave a null-terminated string. | |
77 } | |
78 error = ERR_get_error(); | |
79 } | |
80 } | |
81 | |
82 | |
83 /* Handle an error reported from the BoringSSL library. */ | |
84 static void ThrowIOException(int status, | |
85 const char* exception_type, | |
86 const char* message, | |
87 bool free_message = false) { | |
88 char error_string[SSL_ERROR_MESSAGE_BUFFER_SIZE]; | |
89 FetchErrorString(error_string, SSL_ERROR_MESSAGE_BUFFER_SIZE); | |
90 OSError os_error_struct(status, error_string, OSError::kBoringSSL); | |
91 Dart_Handle os_error = DartUtils::NewDartOSError(&os_error_struct); | |
92 Dart_Handle exception = | |
93 DartUtils::NewDartIOException(exception_type, message, os_error); | |
94 ASSERT(!Dart_IsError(exception)); | |
95 if (free_message) { | |
96 free(const_cast<char*>(message)); | |
97 } | |
98 Dart_ThrowException(exception); | |
99 UNREACHABLE(); | |
100 } | |
101 | |
102 | |
103 static SSLFilter* GetFilter(Dart_NativeArguments args) { | |
104 SSLFilter* filter; | |
105 Dart_Handle dart_this = ThrowIfError(Dart_GetNativeArgument(args, 0)); | |
106 ASSERT(Dart_IsInstance(dart_this)); | |
107 ThrowIfError(Dart_GetNativeInstanceField( | |
108 dart_this, | |
109 kSSLFilterNativeFieldIndex, | |
110 reinterpret_cast<intptr_t*>(&filter))); | |
111 return filter; | |
112 } | |
113 | |
114 | |
115 static void DeleteFilter( | |
116 void* isolate_data, | |
117 Dart_WeakPersistentHandle handle, | |
118 void* context_pointer) { | |
119 SSLFilter* filter = reinterpret_cast<SSLFilter*>(context_pointer); | |
120 delete filter; | |
121 } | |
122 | |
123 | |
124 static Dart_Handle SetFilter(Dart_NativeArguments args, SSLFilter* filter) { | |
125 ASSERT(filter != NULL); | |
126 Dart_Handle dart_this = Dart_GetNativeArgument(args, 0); | |
127 RETURN_IF_ERROR(dart_this); | |
128 ASSERT(Dart_IsInstance(dart_this)); | |
129 Dart_Handle err = Dart_SetNativeInstanceField( | |
130 dart_this, | |
131 kSSLFilterNativeFieldIndex, | |
132 reinterpret_cast<intptr_t>(filter)); | |
133 RETURN_IF_ERROR(err); | |
134 Dart_NewWeakPersistentHandle(dart_this, | |
135 reinterpret_cast<void*>(filter), | |
136 sizeof(*filter), | |
137 DeleteFilter); | |
138 return Dart_Null(); | |
139 } | |
140 | |
141 | |
142 static SSL_CTX* GetSecurityContext(Dart_NativeArguments args) { | |
143 SSL_CTX* context; | |
144 Dart_Handle dart_this = ThrowIfError(Dart_GetNativeArgument(args, 0)); | |
145 ASSERT(Dart_IsInstance(dart_this)); | |
146 ThrowIfError(Dart_GetNativeInstanceField( | |
147 dart_this, | |
148 kSecurityContextNativeFieldIndex, | |
149 reinterpret_cast<intptr_t*>(&context))); | |
150 return context; | |
151 } | |
152 | |
153 | |
154 static void FreeSecurityContext( | |
155 void* isolate_data, | |
156 Dart_WeakPersistentHandle handle, | |
157 void* context_pointer) { | |
158 SSL_CTX* context = static_cast<SSL_CTX*>(context_pointer); | |
159 SSL_CTX_free(context); | |
160 } | |
161 | |
162 | |
163 static Dart_Handle SetSecurityContext(Dart_NativeArguments args, | |
164 SSL_CTX* context) { | |
165 const int approximate_size_of_context = 1500; | |
166 Dart_Handle dart_this = Dart_GetNativeArgument(args, 0); | |
167 RETURN_IF_ERROR(dart_this); | |
168 ASSERT(Dart_IsInstance(dart_this)); | |
169 Dart_Handle err = Dart_SetNativeInstanceField( | |
170 dart_this, | |
171 kSecurityContextNativeFieldIndex, | |
172 reinterpret_cast<intptr_t>(context)); | |
173 RETURN_IF_ERROR(err); | |
174 Dart_NewWeakPersistentHandle(dart_this, | |
175 context, | |
176 approximate_size_of_context, | |
177 FreeSecurityContext); | |
178 return Dart_Null(); | |
179 } | |
180 | |
181 | |
182 static X509* GetX509Certificate(Dart_NativeArguments args) { | |
183 X509* certificate; | |
184 Dart_Handle dart_this = ThrowIfError(Dart_GetNativeArgument(args, 0)); | |
185 ASSERT(Dart_IsInstance(dart_this)); | |
186 ThrowIfError(Dart_GetNativeInstanceField( | |
187 dart_this, | |
188 kX509NativeFieldIndex, | |
189 reinterpret_cast<intptr_t*>(&certificate))); | |
190 return certificate; | |
191 } | |
192 | |
193 | |
194 // Forward declaration. | |
195 static void SetAlpnProtocolList(Dart_Handle protocols_handle, | |
196 SSL* ssl, | |
197 SSL_CTX* context, | |
198 bool is_server); | |
199 | |
200 | |
201 void FUNCTION_NAME(SecureSocket_Init)(Dart_NativeArguments args) { | |
202 Dart_Handle dart_this = ThrowIfError(Dart_GetNativeArgument(args, 0)); | |
203 SSLFilter* filter = new SSLFilter(); | |
204 Dart_Handle err = SetFilter(args, filter); | |
205 if (Dart_IsError(err)) { | |
206 delete filter; | |
207 Dart_PropagateError(err); | |
208 } | |
209 err = filter->Init(dart_this); | |
210 if (Dart_IsError(err)) { | |
211 // The finalizer was set up by SetFilter. It will delete `filter` if there | |
212 // is an error. | |
213 filter->Destroy(); | |
214 Dart_PropagateError(err); | |
215 } | |
216 } | |
217 | |
218 | |
219 void FUNCTION_NAME(SecureSocket_Connect)(Dart_NativeArguments args) { | |
220 Dart_Handle host_name_object = ThrowIfError(Dart_GetNativeArgument(args, 1)); | |
221 Dart_Handle context_object = ThrowIfError(Dart_GetNativeArgument(args, 2)); | |
222 bool is_server = DartUtils::GetBooleanValue(Dart_GetNativeArgument(args, 3)); | |
223 bool request_client_certificate = | |
224 DartUtils::GetBooleanValue(Dart_GetNativeArgument(args, 4)); | |
225 bool require_client_certificate = | |
226 DartUtils::GetBooleanValue(Dart_GetNativeArgument(args, 5)); | |
227 Dart_Handle protocols_handle = | |
228 ThrowIfError(Dart_GetNativeArgument(args, 6)); | |
229 | |
230 const char* host_name = NULL; | |
231 // TODO(whesse): Is truncating a Dart string containing \0 what we want? | |
232 ThrowIfError(Dart_StringToCString(host_name_object, &host_name)); | |
233 | |
234 SSL_CTX* context = NULL; | |
235 if (!Dart_IsNull(context_object)) { | |
236 ThrowIfError(Dart_GetNativeInstanceField( | |
237 context_object, | |
238 kSecurityContextNativeFieldIndex, | |
239 reinterpret_cast<intptr_t*>(&context))); | |
240 } | |
241 | |
242 // The protocols_handle is guaranteed to be a valid Uint8List. | |
243 // It will have the correct length encoding of the protocols array. | |
244 ASSERT(!Dart_IsNull(protocols_handle)); | |
245 | |
246 GetFilter(args)->Connect(host_name, | |
247 context, | |
248 is_server, | |
249 request_client_certificate, | |
250 require_client_certificate, | |
251 protocols_handle); | |
252 } | |
253 | |
254 | |
255 void FUNCTION_NAME(SecureSocket_Destroy)(Dart_NativeArguments args) { | |
256 SSLFilter* filter = GetFilter(args); | |
257 // The SSLFilter is deleted in the finalizer for the Dart object created by | |
258 // SetFilter. There is no need to NULL-out the native field for the SSLFilter | |
259 // here because the SSLFilter won't be deleted until the finalizer for the | |
260 // Dart object runs while the Dart object is being GCd. This approach avoids a | |
261 // leak if Destroy isn't called, and avoids a NULL-dereference if Destroy is | |
262 // called more than once. | |
263 filter->Destroy(); | |
264 } | |
265 | |
266 | |
267 void FUNCTION_NAME(SecureSocket_Handshake)(Dart_NativeArguments args) { | |
268 GetFilter(args)->Handshake(); | |
269 } | |
270 | |
271 | |
272 void FUNCTION_NAME(SecureSocket_GetSelectedProtocol)( | |
273 Dart_NativeArguments args) { | |
274 GetFilter(args)->GetSelectedProtocol(args); | |
275 } | |
276 | |
277 | |
278 void FUNCTION_NAME(SecureSocket_Renegotiate)(Dart_NativeArguments args) { | |
279 bool use_session_cache = | |
280 DartUtils::GetBooleanValue(Dart_GetNativeArgument(args, 1)); | |
281 bool request_client_certificate = | |
282 DartUtils::GetBooleanValue(Dart_GetNativeArgument(args, 2)); | |
283 bool require_client_certificate = | |
284 DartUtils::GetBooleanValue(Dart_GetNativeArgument(args, 3)); | |
285 GetFilter(args)->Renegotiate(use_session_cache, | |
286 request_client_certificate, | |
287 require_client_certificate); | |
288 } | |
289 | |
290 | |
291 void FUNCTION_NAME(SecureSocket_RegisterHandshakeCompleteCallback)( | |
292 Dart_NativeArguments args) { | |
293 Dart_Handle handshake_complete = | |
294 ThrowIfError(Dart_GetNativeArgument(args, 1)); | |
295 if (!Dart_IsClosure(handshake_complete)) { | |
296 Dart_ThrowException(DartUtils::NewDartArgumentError( | |
297 "Illegal argument to RegisterHandshakeCompleteCallback")); | |
298 } | |
299 GetFilter(args)->RegisterHandshakeCompleteCallback(handshake_complete); | |
300 } | |
301 | |
302 | |
303 void FUNCTION_NAME(SecureSocket_RegisterBadCertificateCallback)( | |
304 Dart_NativeArguments args) { | |
305 Dart_Handle callback = | |
306 ThrowIfError(Dart_GetNativeArgument(args, 1)); | |
307 if (!Dart_IsClosure(callback) && !Dart_IsNull(callback)) { | |
308 Dart_ThrowException(DartUtils::NewDartArgumentError( | |
309 "Illegal argument to RegisterBadCertificateCallback")); | |
310 } | |
311 GetFilter(args)->RegisterBadCertificateCallback(callback); | |
312 } | |
313 | |
314 | |
315 void FUNCTION_NAME(SecureSocket_PeerCertificate) | |
316 (Dart_NativeArguments args) { | |
317 Dart_Handle cert = ThrowIfError(GetFilter(args)->PeerCertificate()); | |
318 Dart_SetReturnValue(args, cert); | |
319 } | |
320 | |
321 | |
322 void FUNCTION_NAME(SecureSocket_FilterPointer)(Dart_NativeArguments args) { | |
323 intptr_t filter_pointer = reinterpret_cast<intptr_t>(GetFilter(args)); | |
324 Dart_SetReturnValue(args, Dart_NewInteger(filter_pointer)); | |
325 } | |
326 | |
327 | |
328 static void ReleaseCertificate( | |
329 void* isolate_data, | |
330 Dart_WeakPersistentHandle handle, | |
331 void* context_pointer) { | |
332 X509* cert = reinterpret_cast<X509*>(context_pointer); | |
333 X509_free(cert); | |
334 } | |
335 | |
336 | |
337 // Returns the handle for a Dart object wrapping the X509 certificate object. | |
338 // The caller should own a reference to the X509 object whose reference count | |
339 // won't drop to zero before the ReleaseCertificate finalizer runs. | |
340 static Dart_Handle WrappedX509Certificate(X509* certificate) { | |
341 const intptr_t approximate_size_of_certificate = 1500; | |
342 if (certificate == NULL) { | |
343 return Dart_Null(); | |
344 } | |
345 Dart_Handle x509_type = | |
346 DartUtils::GetDartType(DartUtils::kIOLibURL, "X509Certificate"); | |
347 if (Dart_IsError(x509_type)) { | |
348 X509_free(certificate); | |
349 return x509_type; | |
350 } | |
351 Dart_Handle arguments[] = { NULL }; | |
352 Dart_Handle result = | |
353 Dart_New(x509_type, DartUtils::NewString("_"), 0, arguments); | |
354 if (Dart_IsError(result)) { | |
355 X509_free(certificate); | |
356 return result; | |
357 } | |
358 ASSERT(Dart_IsInstance(result)); | |
359 Dart_Handle status = Dart_SetNativeInstanceField( | |
360 result, | |
361 kX509NativeFieldIndex, | |
362 reinterpret_cast<intptr_t>(certificate)); | |
363 if (Dart_IsError(status)) { | |
364 X509_free(certificate); | |
365 return status; | |
366 } | |
367 Dart_NewWeakPersistentHandle(result, | |
368 reinterpret_cast<void*>(certificate), | |
369 approximate_size_of_certificate, | |
370 ReleaseCertificate); | |
371 return result; | |
372 } | |
373 | |
374 | |
375 int CertificateCallback(int preverify_ok, X509_STORE_CTX* store_ctx) { | |
376 if (preverify_ok == 1) { | |
377 return 1; | |
378 } | |
379 Dart_Isolate isolate = Dart_CurrentIsolate(); | |
380 if (isolate == NULL) { | |
381 FATAL("CertificateCallback called with no current isolate\n"); | |
382 } | |
383 X509* certificate = X509_STORE_CTX_get_current_cert(store_ctx); | |
384 int ssl_index = SSL_get_ex_data_X509_STORE_CTX_idx(); | |
385 SSL* ssl = static_cast<SSL*>( | |
386 X509_STORE_CTX_get_ex_data(store_ctx, ssl_index)); | |
387 SSLFilter* filter = static_cast<SSLFilter*>( | |
388 SSL_get_ex_data(ssl, SSLFilter::filter_ssl_index)); | |
389 Dart_Handle callback = filter->bad_certificate_callback(); | |
390 if (Dart_IsNull(callback)) { | |
391 return 0; | |
392 } | |
393 | |
394 // Upref since the Dart X509 object may outlive the SecurityContext. | |
395 if (certificate != NULL) { | |
396 X509_up_ref(certificate); | |
397 } | |
398 Dart_Handle args[1]; | |
399 args[0] = WrappedX509Certificate(certificate); | |
400 if (Dart_IsError(args[0])) { | |
401 filter->callback_error = args[0]; | |
402 return 0; | |
403 } | |
404 Dart_Handle result = Dart_InvokeClosure(callback, 1, args); | |
405 if (!Dart_IsError(result) && !Dart_IsBoolean(result)) { | |
406 result = Dart_NewUnhandledExceptionError(DartUtils::NewDartIOException( | |
407 "HandshakeException", | |
408 "BadCertificateCallback returned a value that was not a boolean", | |
409 Dart_Null())); | |
410 } | |
411 if (Dart_IsError(result)) { | |
412 filter->callback_error = result; | |
413 return 0; | |
414 } | |
415 return DartUtils::GetBooleanValue(result); | |
416 } | |
417 | |
418 | |
419 void FUNCTION_NAME(SecurityContext_Allocate)(Dart_NativeArguments args) { | |
420 SSLFilter::InitializeLibrary(); | |
421 SSL_CTX* context = SSL_CTX_new(TLS_method()); | |
422 SSL_CTX_set_verify(context, SSL_VERIFY_PEER, CertificateCallback); | |
423 SSL_CTX_set_min_version(context, TLS1_VERSION); | |
424 SSL_CTX_set_cipher_list(context, "HIGH:MEDIUM"); | |
425 SSL_CTX_set_cipher_list_tls11(context, "HIGH:MEDIUM"); | |
426 Dart_Handle err = SetSecurityContext(args, context); | |
427 if (Dart_IsError(err)) { | |
428 SSL_CTX_free(context); | |
429 Dart_PropagateError(err); | |
430 } | |
431 } | |
432 | |
433 | |
434 int PasswordCallback(char* buf, int size, int rwflag, void* userdata) { | |
435 char* password = static_cast<char*>(userdata); | |
436 ASSERT(size == PEM_BUFSIZE); | |
437 strncpy(buf, password, size); | |
438 return strlen(password); | |
439 } | |
440 | |
441 | |
442 void CheckStatus(int status, const char* type, const char* message) { | |
443 // TODO(24183): Take appropriate action on failed calls, | |
444 // throw exception that includes all messages from the error stack. | |
445 if (status == 1) { | |
446 return; | |
447 } | |
448 if (SSL_LOG_STATUS) { | |
449 int error = ERR_get_error(); | |
450 Log::PrintErr("Failed: %s status %d", message, status); | |
451 char error_string[SSL_ERROR_MESSAGE_BUFFER_SIZE]; | |
452 ERR_error_string_n(error, error_string, SSL_ERROR_MESSAGE_BUFFER_SIZE); | |
453 Log::PrintErr("ERROR: %d %s\n", error, error_string); | |
454 } | |
455 ThrowIOException(status, type, message); | |
456 } | |
457 | |
458 | |
459 // Where the argument to the constructor is the handle for an object | |
460 // implementing List<int>, this class creates a scope in which a memory-backed | |
461 // BIO is allocated. Leaving the scope cleans up the BIO and the buffer that | |
462 // was used to create it. | |
463 // | |
464 // Do not make Dart_ API calls while in a ScopedMemBIO. | |
465 // Do not call Dart_PropagateError while in a ScopedMemBIO. | |
466 class ScopedMemBIO { | |
467 public: | |
468 explicit ScopedMemBIO(Dart_Handle object) { | |
469 if (!Dart_IsTypedData(object) && !Dart_IsList(object)) { | |
470 Dart_ThrowException(DartUtils::NewDartArgumentError( | |
471 "Argument is not a List<int>")); | |
472 } | |
473 | |
474 uint8_t* bytes = NULL; | |
475 intptr_t bytes_len = 0; | |
476 bool is_typed_data = false; | |
477 if (Dart_IsTypedData(object)) { | |
478 is_typed_data = true; | |
479 Dart_TypedData_Type typ; | |
480 ThrowIfError(Dart_TypedDataAcquireData( | |
481 object, | |
482 &typ, | |
483 reinterpret_cast<void**>(&bytes), | |
484 &bytes_len)); | |
485 } else { | |
486 ASSERT(Dart_IsList(object)); | |
487 ThrowIfError(Dart_ListLength(object, &bytes_len)); | |
488 bytes = Dart_ScopeAllocate(bytes_len); | |
489 ASSERT(bytes != NULL); | |
490 ThrowIfError(Dart_ListGetAsBytes(object, 0, bytes, bytes_len)); | |
491 } | |
492 | |
493 object_ = object; | |
494 bytes_ = bytes; | |
495 bytes_len_ = bytes_len; | |
496 bio_ = BIO_new_mem_buf(bytes, bytes_len); | |
497 ASSERT(bio_ != NULL); | |
498 is_typed_data_ = is_typed_data; | |
499 } | |
500 | |
501 ~ScopedMemBIO() { | |
502 ASSERT(bio_ != NULL); | |
503 if (is_typed_data_) { | |
504 BIO_free(bio_); | |
505 ThrowIfError(Dart_TypedDataReleaseData(object_)); | |
506 } else { | |
507 BIO_free(bio_); | |
508 } | |
509 } | |
510 | |
511 BIO* bio() { | |
512 ASSERT(bio_ != NULL); | |
513 return bio_; | |
514 } | |
515 | |
516 private: | |
517 Dart_Handle object_; | |
518 uint8_t* bytes_; | |
519 intptr_t bytes_len_; | |
520 BIO* bio_; | |
521 bool is_typed_data_; | |
522 | |
523 DISALLOW_ALLOCATION(); | |
524 DISALLOW_COPY_AND_ASSIGN(ScopedMemBIO); | |
525 }; | |
526 | |
527 template<typename T, void (*free_func)(T*)> | |
528 class ScopedSSLType { | |
529 public: | |
530 explicit ScopedSSLType(T* obj) : obj_(obj) {} | |
531 | |
532 ~ScopedSSLType() { | |
533 if (obj_ != NULL) { | |
534 free_func(obj_); | |
535 } | |
536 } | |
537 | |
538 T* get() { return obj_; } | |
539 const T* get() const { return obj_; } | |
540 | |
541 T* release() { | |
542 T* result = obj_; | |
543 obj_ = NULL; | |
544 return result; | |
545 } | |
546 | |
547 private: | |
548 T* obj_; | |
549 | |
550 DISALLOW_ALLOCATION(); | |
551 DISALLOW_COPY_AND_ASSIGN(ScopedSSLType); | |
552 }; | |
553 | |
554 template<typename T, typename E, void (*func)(E*)> | |
555 class ScopedSSLStackType { | |
556 public: | |
557 explicit ScopedSSLStackType(T* obj) : obj_(obj) {} | |
558 | |
559 ~ScopedSSLStackType() { | |
560 if (obj_ != NULL) { | |
561 sk_pop_free(reinterpret_cast<_STACK*>(obj_), | |
562 reinterpret_cast<void (*)(void *)>(func)); | |
563 } | |
564 } | |
565 | |
566 T* get() { return obj_; } | |
567 const T* get() const { return obj_; } | |
568 | |
569 T* release() { | |
570 T* result = obj_; | |
571 obj_ = NULL; | |
572 return result; | |
573 } | |
574 | |
575 private: | |
576 T* obj_; | |
577 | |
578 DISALLOW_ALLOCATION(); | |
579 DISALLOW_COPY_AND_ASSIGN(ScopedSSLStackType); | |
580 }; | |
581 | |
582 typedef ScopedSSLType<PKCS12, PKCS12_free> ScopedPKCS12; | |
583 typedef ScopedSSLType<X509, X509_free> ScopedX509; | |
584 typedef ScopedSSLStackType<STACK_OF(X509), X509, X509_free> ScopedX509Stack; | |
585 | |
586 static bool NoPEMStartLine() { | |
587 uint32_t last_error = ERR_peek_last_error(); | |
588 return (ERR_GET_LIB(last_error) == ERR_LIB_PEM) && | |
589 (ERR_GET_REASON(last_error) == PEM_R_NO_START_LINE); | |
590 } | |
591 | |
592 | |
593 static EVP_PKEY* GetPrivateKeyPKCS12(BIO* bio, const char* password) { | |
594 ScopedPKCS12 p12(d2i_PKCS12_bio(bio, NULL)); | |
595 if (p12.get() == NULL) { | |
596 return NULL; | |
597 } | |
598 | |
599 EVP_PKEY* key = NULL; | |
600 X509 *cert = NULL; | |
601 STACK_OF(X509) *ca_certs = NULL; | |
602 int status = PKCS12_parse(p12.get(), password, &key, &cert, &ca_certs); | |
603 if (status == 0) { | |
604 return NULL; | |
605 } | |
606 | |
607 // We only care about the private key. | |
608 ScopedX509 delete_cert(cert); | |
609 ScopedX509Stack delete_ca_certs(ca_certs); | |
610 return key; | |
611 } | |
612 | |
613 | |
614 static EVP_PKEY* GetPrivateKey(BIO* bio, const char* password) { | |
615 EVP_PKEY *key = PEM_read_bio_PrivateKey( | |
616 bio, NULL, PasswordCallback, const_cast<char*>(password)); | |
617 if (key == NULL) { | |
618 // We try reading data as PKCS12 only if reading as PEM was unsuccessful and | |
619 // if there is no indication that the data is malformed PEM. We assume the | |
620 // data is malformed PEM if it contains the start line, i.e. a line | |
621 // with ----- BEGIN. | |
622 if (NoPEMStartLine()) { | |
623 // Reset the bio, and clear the error from trying to read as PEM. | |
624 ERR_clear_error(); | |
625 BIO_reset(bio); | |
626 | |
627 // Try to decode as PKCS12. | |
628 key = GetPrivateKeyPKCS12(bio, password); | |
629 } | |
630 } | |
631 return key; | |
632 } | |
633 | |
634 | |
635 static const char* GetPasswordArgument(Dart_NativeArguments args, | |
636 intptr_t index) { | |
637 Dart_Handle password_object = | |
638 ThrowIfError(Dart_GetNativeArgument(args, index)); | |
639 const char* password = NULL; | |
640 if (Dart_IsString(password_object)) { | |
641 ThrowIfError(Dart_StringToCString(password_object, &password)); | |
642 if (strlen(password) > PEM_BUFSIZE - 1) { | |
643 Dart_ThrowException(DartUtils::NewDartArgumentError( | |
644 "Password length is greater than 1023 (PEM_BUFSIZE)")); | |
645 } | |
646 } else if (Dart_IsNull(password_object)) { | |
647 password = ""; | |
648 } else { | |
649 Dart_ThrowException(DartUtils::NewDartArgumentError( | |
650 "Password is not a String or null")); | |
651 } | |
652 return password; | |
653 } | |
654 | |
655 | |
656 void FUNCTION_NAME(SecurityContext_UsePrivateKeyBytes)( | |
657 Dart_NativeArguments args) { | |
658 SSL_CTX* context = GetSecurityContext(args); | |
659 const char* password = GetPasswordArgument(args, 2); | |
660 | |
661 int status; | |
662 { | |
663 ScopedMemBIO bio(ThrowIfError(Dart_GetNativeArgument(args, 1))); | |
664 EVP_PKEY *key = GetPrivateKey(bio.bio(), password); | |
665 status = SSL_CTX_use_PrivateKey(context, key); | |
666 } | |
667 | |
668 // TODO(24184): Handle different expected errors here - file missing, | |
669 // incorrect password, file not a PEM, and throw exceptions. | |
670 // CheckStatus should also throw an exception in uncaught cases. | |
671 CheckStatus(status, "TlsException", "Failure in usePrivateKeyBytes"); | |
672 } | |
673 | |
674 | |
675 static int SetTrustedCertificatesBytesPKCS12(SSL_CTX* context, | |
676 BIO* bio, | |
677 const char* password) { | |
678 ScopedPKCS12 p12(d2i_PKCS12_bio(bio, NULL)); | |
679 if (p12.get() == NULL) { | |
680 return NULL; | |
681 } | |
682 | |
683 EVP_PKEY* key = NULL; | |
684 X509 *cert = NULL; | |
685 STACK_OF(X509) *ca_certs = NULL; | |
686 int status = PKCS12_parse(p12.get(), password, &key, &cert, &ca_certs); | |
687 if (status == 0) { | |
688 return status; | |
689 } | |
690 | |
691 ScopedX509Stack cert_stack(ca_certs); | |
692 X509_STORE* store = SSL_CTX_get_cert_store(context); | |
693 status = X509_STORE_add_cert(store, cert); | |
694 if (status == 0) { | |
695 X509_free(cert); | |
696 return status; | |
697 } | |
698 | |
699 X509* ca; | |
700 while ((ca = sk_X509_shift(cert_stack.get())) != NULL) { | |
701 status = X509_STORE_add_cert(store, ca); | |
702 if (status == 0) { | |
703 X509_free(ca); | |
704 return status; | |
705 } | |
706 } | |
707 | |
708 return status; | |
709 } | |
710 | |
711 | |
712 static int SetTrustedCertificatesBytesPEM(SSL_CTX* context, BIO* bio) { | |
713 X509_STORE* store = SSL_CTX_get_cert_store(context); | |
714 | |
715 int status = 0; | |
716 X509* cert = NULL; | |
717 while ((cert = PEM_read_bio_X509(bio, NULL, NULL, NULL)) != NULL) { | |
718 status = X509_STORE_add_cert(store, cert); | |
719 if (status == 0) { | |
720 X509_free(cert); | |
721 return status; | |
722 } | |
723 } | |
724 | |
725 // If no PEM start line is found, it means that we read to the end of the | |
726 // file, or that the file isn't PEM. In the first case, status will be | |
727 // non-zero indicating success. In the second case, status will be 0, | |
728 // indicating that we should try to read as PKCS12. If there is some other | |
729 // error, we return it up to the caller. | |
730 return NoPEMStartLine() ? status : 0; | |
731 } | |
732 | |
733 | |
734 static int SetTrustedCertificatesBytes(SSL_CTX* context, | |
735 BIO* bio, | |
736 const char* password) { | |
737 int status = SetTrustedCertificatesBytesPEM(context, bio); | |
738 if (status == 0) { | |
739 if (NoPEMStartLine()) { | |
740 ERR_clear_error(); | |
741 BIO_reset(bio); | |
742 status = SetTrustedCertificatesBytesPKCS12(context, bio, password); | |
743 } | |
744 } else { | |
745 // The PEM file was successfully parsed. | |
746 ERR_clear_error(); | |
747 } | |
748 return status; | |
749 } | |
750 | |
751 | |
752 void FUNCTION_NAME(SecurityContext_SetTrustedCertificatesBytes)( | |
753 Dart_NativeArguments args) { | |
754 SSL_CTX* context = GetSecurityContext(args); | |
755 const char* password = GetPasswordArgument(args, 2); | |
756 int status; | |
757 { | |
758 ScopedMemBIO bio(ThrowIfError(Dart_GetNativeArgument(args, 1))); | |
759 status = SetTrustedCertificatesBytes(context, bio.bio(), password); | |
760 } | |
761 CheckStatus(status, | |
762 "TlsException", | |
763 "Failure in setTrustedCertificatesBytes"); | |
764 } | |
765 | |
766 | |
767 void FUNCTION_NAME(SecurityContext_TrustBuiltinRoots)( | |
768 Dart_NativeArguments args) { | |
769 SSL_CTX* context = GetSecurityContext(args); | |
770 X509_STORE* store = SSL_CTX_get_cert_store(context); | |
771 BIO* roots_bio = | |
772 BIO_new_mem_buf(const_cast<unsigned char*>(root_certificates_pem), | |
773 root_certificates_pem_length); | |
774 X509* root_cert; | |
775 // PEM_read_bio_X509 reads PEM-encoded certificates from a bio (in our case, | |
776 // backed by a memory buffer), and returns X509 objects, one by one. | |
777 // When the end of the bio is reached, it returns null. | |
778 while ((root_cert = PEM_read_bio_X509(roots_bio, NULL, NULL, NULL))) { | |
779 X509_STORE_add_cert(store, root_cert); | |
780 } | |
781 BIO_free(roots_bio); | |
782 } | |
783 | |
784 | |
785 static int UseChainBytesPKCS12(SSL_CTX* context, | |
786 BIO* bio, | |
787 const char* password) { | |
788 ScopedPKCS12 p12(d2i_PKCS12_bio(bio, NULL)); | |
789 if (p12.get() == NULL) { | |
790 return NULL; | |
791 } | |
792 | |
793 EVP_PKEY* key = NULL; | |
794 X509 *cert = NULL; | |
795 STACK_OF(X509) *ca_certs = NULL; | |
796 int status = PKCS12_parse(p12.get(), password, &key, &cert, &ca_certs); | |
797 if (status == 0) { | |
798 return status; | |
799 } | |
800 | |
801 ScopedX509 x509(cert); | |
802 ScopedX509Stack certs(ca_certs); | |
803 status = SSL_CTX_use_certificate(context, x509.get()); | |
804 if (ERR_peek_error() != 0) { | |
805 // Key/certificate mismatch doesn't imply status is 0. | |
806 status = 0; | |
807 } | |
808 if (status == 0) { | |
809 return status; | |
810 } | |
811 | |
812 SSL_CTX_clear_chain_certs(context); | |
813 | |
814 X509* ca; | |
815 while ((ca = sk_X509_shift(certs.get())) != NULL) { | |
816 status = SSL_CTX_add0_chain_cert(context, ca); | |
817 if (status == 0) { | |
818 X509_free(ca); | |
819 return status; | |
820 } | |
821 } | |
822 | |
823 return status; | |
824 } | |
825 | |
826 | |
827 static int UseChainBytesPEM(SSL_CTX* context, BIO* bio) { | |
828 int status = 0; | |
829 ScopedX509 x509(PEM_read_bio_X509_AUX(bio, NULL, NULL, NULL)); | |
830 if (x509.get() == NULL) { | |
831 return 0; | |
832 } | |
833 | |
834 status = SSL_CTX_use_certificate(context, x509.get()); | |
835 if (ERR_peek_error() != 0) { | |
836 // Key/certificate mismatch doesn't imply status is 0. | |
837 status = 0; | |
838 } | |
839 if (status == 0) { | |
840 return status; | |
841 } | |
842 | |
843 SSL_CTX_clear_chain_certs(context); | |
844 | |
845 X509* ca; | |
846 while ((ca = PEM_read_bio_X509(bio, NULL, NULL, NULL)) != NULL) { | |
847 status = SSL_CTX_add0_chain_cert(context, ca); | |
848 if (status == 0) { | |
849 X509_free(ca); | |
850 return status; | |
851 } | |
852 // Note that we must not free `ca` if it was successfully added to the | |
853 // chain. We must free the main certificate x509, though since its reference | |
854 // count is increased by SSL_CTX_use_certificate. | |
855 } | |
856 | |
857 return NoPEMStartLine() ? status : 0; | |
858 } | |
859 | |
860 | |
861 static int UseChainBytes(SSL_CTX* context, BIO* bio, const char* password) { | |
862 int status = UseChainBytesPEM(context, bio); | |
863 if (status == 0) { | |
864 if (NoPEMStartLine()) { | |
865 ERR_clear_error(); | |
866 BIO_reset(bio); | |
867 status = UseChainBytesPKCS12(context, bio, password); | |
868 } | |
869 } else { | |
870 // The PEM file was successfully read. | |
871 ERR_clear_error(); | |
872 } | |
873 return status; | |
874 } | |
875 | |
876 | |
877 void FUNCTION_NAME(SecurityContext_UseCertificateChainBytes)( | |
878 Dart_NativeArguments args) { | |
879 SSL_CTX* context = GetSecurityContext(args); | |
880 const char* password = GetPasswordArgument(args, 2); | |
881 int status; | |
882 { | |
883 ScopedMemBIO bio(ThrowIfError(Dart_GetNativeArgument(args, 1))); | |
884 status = UseChainBytes(context, bio.bio(), password); | |
885 } | |
886 CheckStatus(status, | |
887 "TlsException", | |
888 "Failure in useCertificateChainBytes"); | |
889 } | |
890 | |
891 | |
892 static int SetClientAuthoritiesPKCS12(SSL_CTX* context, | |
893 BIO* bio, | |
894 const char* password) { | |
895 ScopedPKCS12 p12(d2i_PKCS12_bio(bio, NULL)); | |
896 if (p12.get() == NULL) { | |
897 return 0; | |
898 } | |
899 | |
900 EVP_PKEY* key = NULL; | |
901 X509 *cert = NULL; | |
902 STACK_OF(X509) *ca_certs = NULL; | |
903 int status = PKCS12_parse(p12.get(), password, &key, &cert, &ca_certs); | |
904 if (status == 0) { | |
905 return status; | |
906 } | |
907 | |
908 ScopedX509Stack cert_stack(ca_certs); | |
909 status = SSL_CTX_add_client_CA(context, cert); | |
910 if (status == 0) { | |
911 X509_free(cert); | |
912 return status; | |
913 } | |
914 | |
915 X509* ca; | |
916 while ((ca = sk_X509_shift(cert_stack.get())) != NULL) { | |
917 status = SSL_CTX_add_client_CA(context, ca); | |
918 X509_free(ca); // The name has been extracted. | |
919 if (status == 0) { | |
920 return status; | |
921 } | |
922 } | |
923 | |
924 return status; | |
925 } | |
926 | |
927 | |
928 static int SetClientAuthoritiesPEM(SSL_CTX* context, BIO* bio) { | |
929 int status = 0; | |
930 X509* cert = NULL; | |
931 while ((cert = PEM_read_bio_X509(bio, NULL, NULL, NULL)) != NULL) { | |
932 status = SSL_CTX_add_client_CA(context, cert); | |
933 X509_free(cert); // The name has been extracted. | |
934 if (status == 0) { | |
935 return status; | |
936 } | |
937 } | |
938 return NoPEMStartLine() ? status : 0; | |
939 } | |
940 | |
941 | |
942 static int SetClientAuthorities(SSL_CTX* context, | |
943 BIO* bio, | |
944 const char* password) { | |
945 int status = SetClientAuthoritiesPEM(context, bio); | |
946 if (status == 0) { | |
947 if (NoPEMStartLine()) { | |
948 ERR_clear_error(); | |
949 BIO_reset(bio); | |
950 status = SetClientAuthoritiesPKCS12(context, bio, password); | |
951 } | |
952 } else { | |
953 // The PEM file was successfully parsed. | |
954 ERR_clear_error(); | |
955 } | |
956 return status; | |
957 } | |
958 | |
959 | |
960 void FUNCTION_NAME(SecurityContext_SetClientAuthoritiesBytes)( | |
961 Dart_NativeArguments args) { | |
962 SSL_CTX* context = GetSecurityContext(args); | |
963 const char* password = GetPasswordArgument(args, 2); | |
964 | |
965 int status; | |
966 { | |
967 ScopedMemBIO bio(ThrowIfError(Dart_GetNativeArgument(args, 1))); | |
968 status = SetClientAuthorities(context, bio.bio(), password); | |
969 } | |
970 | |
971 CheckStatus(status, | |
972 "TlsException", | |
973 "Failure in setClientAuthoritiesBytes"); | |
974 } | |
975 | |
976 | |
977 void FUNCTION_NAME(SecurityContext_SetAlpnProtocols)( | |
978 Dart_NativeArguments args) { | |
979 SSL_CTX* context = GetSecurityContext(args); | |
980 Dart_Handle protocols_handle = | |
981 ThrowIfError(Dart_GetNativeArgument(args, 1)); | |
982 Dart_Handle is_server_handle = | |
983 ThrowIfError(Dart_GetNativeArgument(args, 2)); | |
984 if (Dart_IsBoolean(is_server_handle)) { | |
985 bool is_server = DartUtils::GetBooleanValue(is_server_handle); | |
986 SetAlpnProtocolList(protocols_handle, NULL, context, is_server); | |
987 } else { | |
988 Dart_ThrowException(DartUtils::NewDartArgumentError( | |
989 "Non-boolean is_server argument passed to SetAlpnProtocols")); | |
990 } | |
991 } | |
992 | |
993 | |
994 void FUNCTION_NAME(X509_Subject)( | |
995 Dart_NativeArguments args) { | |
996 X509* certificate = GetX509Certificate(args); | |
997 X509_NAME* subject = X509_get_subject_name(certificate); | |
998 char* subject_string = X509_NAME_oneline(subject, NULL, 0); | |
999 Dart_SetReturnValue(args, Dart_NewStringFromCString(subject_string)); | |
1000 OPENSSL_free(subject_string); | |
1001 } | |
1002 | |
1003 | |
1004 void FUNCTION_NAME(X509_Issuer)( | |
1005 Dart_NativeArguments args) { | |
1006 X509* certificate = GetX509Certificate(args); | |
1007 X509_NAME* issuer = X509_get_issuer_name(certificate); | |
1008 char* issuer_string = X509_NAME_oneline(issuer, NULL, 0); | |
1009 Dart_SetReturnValue(args, Dart_NewStringFromCString(issuer_string)); | |
1010 OPENSSL_free(issuer_string); | |
1011 } | |
1012 | |
1013 static Dart_Handle ASN1TimeToMilliseconds(ASN1_TIME* aTime) { | |
1014 ASN1_UTCTIME* epoch_start = M_ASN1_UTCTIME_new(); | |
1015 ASN1_UTCTIME_set_string(epoch_start, "700101000000Z"); | |
1016 int days; | |
1017 int seconds; | |
1018 int result = ASN1_TIME_diff(&days, &seconds, epoch_start, aTime); | |
1019 M_ASN1_UTCTIME_free(epoch_start); | |
1020 if (result != 1) { | |
1021 // TODO(whesse): Propagate an error to Dart. | |
1022 Log::PrintErr("ASN1Time error %d\n", result); | |
1023 } | |
1024 return Dart_NewInteger((86400LL * days + seconds) * 1000LL); | |
1025 } | |
1026 | |
1027 void FUNCTION_NAME(X509_StartValidity)( | |
1028 Dart_NativeArguments args) { | |
1029 X509* certificate = GetX509Certificate(args); | |
1030 ASN1_TIME* not_before = X509_get_notBefore(certificate); | |
1031 Dart_SetReturnValue(args, ASN1TimeToMilliseconds(not_before)); | |
1032 } | |
1033 | |
1034 | |
1035 void FUNCTION_NAME(X509_EndValidity)( | |
1036 Dart_NativeArguments args) { | |
1037 X509* certificate = GetX509Certificate(args); | |
1038 ASN1_TIME* not_after = X509_get_notAfter(certificate); | |
1039 Dart_SetReturnValue(args, ASN1TimeToMilliseconds(not_after)); | |
1040 } | |
1041 | |
1042 | |
1043 /** | |
1044 * Pushes data through the SSL filter, reading and writing from circular | |
1045 * buffers shared with Dart. | |
1046 * | |
1047 * The Dart _SecureFilterImpl class contains 4 ExternalByteArrays used to | |
1048 * pass encrypted and plaintext data to and from the C++ SSLFilter object. | |
1049 * | |
1050 * ProcessFilter is called with a CObject array containing the pointer to | |
1051 * the SSLFilter, encoded as an int, and the start and end positions of the | |
1052 * valid data in the four circular buffers. The function only reads from | |
1053 * the valid data area of the input buffers, and only writes to the free | |
1054 * area of the output buffers. The function returns the new start and end | |
1055 * positions in the buffers, but it only updates start for input buffers, and | |
1056 * end for output buffers. Therefore, the Dart thread can simultaneously | |
1057 * write to the free space and end pointer of input buffers, and read from | |
1058 * the data space of output buffers, and modify the start pointer. | |
1059 * | |
1060 * When ProcessFilter returns, the Dart thread is responsible for combining | |
1061 * the updated pointers from Dart and C++, to make the new valid state of | |
1062 * the circular buffer. | |
1063 */ | |
1064 CObject* SSLFilter::ProcessFilterRequest(const CObjectArray& request) { | |
1065 CObjectIntptr filter_object(request[0]); | |
1066 SSLFilter* filter = reinterpret_cast<SSLFilter*>(filter_object.Value()); | |
1067 bool in_handshake = CObjectBool(request[1]).Value(); | |
1068 int starts[SSLFilter::kNumBuffers]; | |
1069 int ends[SSLFilter::kNumBuffers]; | |
1070 for (int i = 0; i < SSLFilter::kNumBuffers; ++i) { | |
1071 starts[i] = CObjectInt32(request[2 * i + 2]).Value(); | |
1072 ends[i] = CObjectInt32(request[2 * i + 3]).Value(); | |
1073 } | |
1074 | |
1075 if (filter->ProcessAllBuffers(starts, ends, in_handshake)) { | |
1076 CObjectArray* result = new CObjectArray( | |
1077 CObject::NewArray(SSLFilter::kNumBuffers * 2)); | |
1078 for (int i = 0; i < SSLFilter::kNumBuffers; ++i) { | |
1079 result->SetAt(2 * i, new CObjectInt32(CObject::NewInt32(starts[i]))); | |
1080 result->SetAt(2 * i + 1, new CObjectInt32(CObject::NewInt32(ends[i]))); | |
1081 } | |
1082 return result; | |
1083 } else { | |
1084 int32_t error_code = static_cast<int32_t>(ERR_peek_error()); | |
1085 char error_string[SSL_ERROR_MESSAGE_BUFFER_SIZE]; | |
1086 FetchErrorString(error_string, SSL_ERROR_MESSAGE_BUFFER_SIZE); | |
1087 CObjectArray* result = new CObjectArray(CObject::NewArray(2)); | |
1088 result->SetAt(0, new CObjectInt32(CObject::NewInt32(error_code))); | |
1089 result->SetAt(1, new CObjectString(CObject::NewString(error_string))); | |
1090 return result; | |
1091 } | |
1092 } | |
1093 | |
1094 | |
1095 bool SSLFilter::ProcessAllBuffers(int starts[kNumBuffers], | |
1096 int ends[kNumBuffers], | |
1097 bool in_handshake) { | |
1098 for (int i = 0; i < kNumBuffers; ++i) { | |
1099 if (in_handshake && (i == kReadPlaintext || i == kWritePlaintext)) continue; | |
1100 int start = starts[i]; | |
1101 int end = ends[i]; | |
1102 int size = isBufferEncrypted(i) ? encrypted_buffer_size_ : buffer_size_; | |
1103 if (start < 0 || end < 0 || start >= size || end >= size) { | |
1104 FATAL("Out-of-bounds internal buffer access in dart:io SecureSocket"); | |
1105 } | |
1106 switch (i) { | |
1107 case kReadPlaintext: | |
1108 case kWriteEncrypted: | |
1109 // Write data to the circular buffer's free space. If the buffer | |
1110 // is full, neither if statement is executed and nothing happens. | |
1111 if (start <= end) { | |
1112 // If the free space may be split into two segments, | |
1113 // then the first is [end, size), unless start == 0. | |
1114 // Then, since the last free byte is at position start - 2, | |
1115 // the interval is [end, size - 1). | |
1116 int buffer_end = (start == 0) ? size - 1 : size; | |
1117 int bytes = (i == kReadPlaintext) ? | |
1118 ProcessReadPlaintextBuffer(end, buffer_end) : | |
1119 ProcessWriteEncryptedBuffer(end, buffer_end); | |
1120 if (bytes < 0) return false; | |
1121 end += bytes; | |
1122 ASSERT(end <= size); | |
1123 if (end == size) end = 0; | |
1124 } | |
1125 if (start > end + 1) { | |
1126 int bytes = (i == kReadPlaintext) ? | |
1127 ProcessReadPlaintextBuffer(end, start - 1) : | |
1128 ProcessWriteEncryptedBuffer(end, start - 1); | |
1129 if (bytes < 0) return false; | |
1130 end += bytes; | |
1131 ASSERT(end < start); | |
1132 } | |
1133 ends[i] = end; | |
1134 break; | |
1135 case kReadEncrypted: | |
1136 case kWritePlaintext: | |
1137 // Read/Write data from circular buffer. If the buffer is empty, | |
1138 // neither if statement's condition is true. | |
1139 if (end < start) { | |
1140 // Data may be split into two segments. In this case, | |
1141 // the first is [start, size). | |
1142 int bytes = (i == kReadEncrypted) ? | |
1143 ProcessReadEncryptedBuffer(start, size) : | |
1144 ProcessWritePlaintextBuffer(start, size); | |
1145 if (bytes < 0) return false; | |
1146 start += bytes; | |
1147 ASSERT(start <= size); | |
1148 if (start == size) start = 0; | |
1149 } | |
1150 if (start < end) { | |
1151 int bytes = (i == kReadEncrypted) ? | |
1152 ProcessReadEncryptedBuffer(start, end) : | |
1153 ProcessWritePlaintextBuffer(start, end); | |
1154 if (bytes < 0) return false; | |
1155 start += bytes; | |
1156 ASSERT(start <= end); | |
1157 } | |
1158 starts[i] = start; | |
1159 break; | |
1160 default: | |
1161 UNREACHABLE(); | |
1162 } | |
1163 } | |
1164 return true; | |
1165 } | |
1166 | |
1167 | |
1168 Dart_Handle SSLFilter::Init(Dart_Handle dart_this) { | |
1169 if (!library_initialized_) { | |
1170 InitializeLibrary(); | |
1171 } | |
1172 ASSERT(string_start_ == NULL); | |
1173 string_start_ = Dart_NewPersistentHandle(DartUtils::NewString("start")); | |
1174 ASSERT(string_start_ != NULL); | |
1175 ASSERT(string_length_ == NULL); | |
1176 string_length_ = Dart_NewPersistentHandle(DartUtils::NewString("length")); | |
1177 ASSERT(string_length_ != NULL); | |
1178 ASSERT(bad_certificate_callback_ == NULL); | |
1179 bad_certificate_callback_ = Dart_NewPersistentHandle(Dart_Null()); | |
1180 ASSERT(bad_certificate_callback_ != NULL); | |
1181 | |
1182 // Caller handles cleanup on an error. | |
1183 return InitializeBuffers(dart_this); | |
1184 } | |
1185 | |
1186 | |
1187 Dart_Handle SSLFilter::InitializeBuffers(Dart_Handle dart_this) { | |
1188 // Create SSLFilter buffers as ExternalUint8Array objects. | |
1189 Dart_Handle buffers_string = DartUtils::NewString("buffers"); | |
1190 RETURN_IF_ERROR(buffers_string); | |
1191 Dart_Handle dart_buffers_object = Dart_GetField(dart_this, buffers_string); | |
1192 RETURN_IF_ERROR(dart_buffers_object); | |
1193 Dart_Handle secure_filter_impl_type = Dart_InstanceGetType(dart_this); | |
1194 RETURN_IF_ERROR(secure_filter_impl_type); | |
1195 Dart_Handle size_string = DartUtils::NewString("SIZE"); | |
1196 RETURN_IF_ERROR(size_string); | |
1197 Dart_Handle dart_buffer_size = Dart_GetField( | |
1198 secure_filter_impl_type, size_string); | |
1199 RETURN_IF_ERROR(dart_buffer_size); | |
1200 | |
1201 int64_t buffer_size = 0; | |
1202 Dart_Handle err = Dart_IntegerToInt64(dart_buffer_size, &buffer_size); | |
1203 RETURN_IF_ERROR(err); | |
1204 | |
1205 Dart_Handle encrypted_size_string = DartUtils::NewString("ENCRYPTED_SIZE"); | |
1206 RETURN_IF_ERROR(encrypted_size_string); | |
1207 | |
1208 Dart_Handle dart_encrypted_buffer_size = Dart_GetField( | |
1209 secure_filter_impl_type, encrypted_size_string); | |
1210 RETURN_IF_ERROR(dart_encrypted_buffer_size); | |
1211 | |
1212 int64_t encrypted_buffer_size = 0; | |
1213 err = Dart_IntegerToInt64(dart_encrypted_buffer_size, &encrypted_buffer_size); | |
1214 RETURN_IF_ERROR(err); | |
1215 | |
1216 if (buffer_size <= 0 || buffer_size > 1 * MB) { | |
1217 FATAL("Invalid buffer size in _ExternalBuffer"); | |
1218 } | |
1219 if (encrypted_buffer_size <= 0 || encrypted_buffer_size > 1 * MB) { | |
1220 FATAL("Invalid encrypted buffer size in _ExternalBuffer"); | |
1221 } | |
1222 buffer_size_ = static_cast<int>(buffer_size); | |
1223 encrypted_buffer_size_ = static_cast<int>(encrypted_buffer_size); | |
1224 | |
1225 Dart_Handle data_identifier = DartUtils::NewString("data"); | |
1226 RETURN_IF_ERROR(data_identifier); | |
1227 | |
1228 for (int i = 0; i < kNumBuffers; i++) { | |
1229 int size = isBufferEncrypted(i) ? encrypted_buffer_size_ : buffer_size_; | |
1230 buffers_[i] = new uint8_t[size]; | |
1231 ASSERT(buffers_[i] != NULL); | |
1232 dart_buffer_objects_[i] = NULL; | |
1233 } | |
1234 | |
1235 Dart_Handle result = Dart_Null(); | |
1236 for (int i = 0; i < kNumBuffers; ++i) { | |
1237 int size = isBufferEncrypted(i) ? encrypted_buffer_size_ : buffer_size_; | |
1238 result = Dart_ListGetAt(dart_buffers_object, i); | |
1239 if (Dart_IsError(result)) { | |
1240 break; | |
1241 } | |
1242 | |
1243 dart_buffer_objects_[i] = Dart_NewPersistentHandle(result); | |
1244 ASSERT(dart_buffer_objects_[i] != NULL); | |
1245 Dart_Handle data = | |
1246 Dart_NewExternalTypedData(Dart_TypedData_kUint8, buffers_[i], size); | |
1247 if (Dart_IsError(data)) { | |
1248 result = data; | |
1249 break; | |
1250 } | |
1251 result = Dart_HandleFromPersistent(dart_buffer_objects_[i]); | |
1252 if (Dart_IsError(result)) { | |
1253 break; | |
1254 } | |
1255 result = Dart_SetField(result, data_identifier, data); | |
1256 if (Dart_IsError(result)) { | |
1257 break; | |
1258 } | |
1259 } | |
1260 | |
1261 // Caller handles cleanup on an error. | |
1262 return result; | |
1263 } | |
1264 | |
1265 | |
1266 void SSLFilter::RegisterHandshakeCompleteCallback(Dart_Handle complete) { | |
1267 ASSERT(NULL == handshake_complete_); | |
1268 handshake_complete_ = Dart_NewPersistentHandle(complete); | |
1269 | |
1270 ASSERT(handshake_complete_ != NULL); | |
1271 } | |
1272 | |
1273 | |
1274 void SSLFilter::RegisterBadCertificateCallback(Dart_Handle callback) { | |
1275 ASSERT(bad_certificate_callback_ != NULL); | |
1276 Dart_DeletePersistentHandle(bad_certificate_callback_); | |
1277 bad_certificate_callback_ = Dart_NewPersistentHandle(callback); | |
1278 ASSERT(bad_certificate_callback_ != NULL); | |
1279 } | |
1280 | |
1281 | |
1282 void SSLFilter::InitializeLibrary() { | |
1283 MutexLocker locker(mutex_); | |
1284 if (!library_initialized_) { | |
1285 SSL_library_init(); | |
1286 filter_ssl_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL); | |
1287 ASSERT(filter_ssl_index >= 0); | |
1288 library_initialized_ = true; | |
1289 } | |
1290 } | |
1291 | |
1292 | |
1293 Dart_Handle SSLFilter::PeerCertificate() { | |
1294 // SSL_get_peer_certificate incs the refcount of certificate. X509_free is | |
1295 // called by the finalizer set up by WrappedX509Certificate. | |
1296 X509* certificate = SSL_get_peer_certificate(ssl_); | |
1297 return WrappedX509Certificate(certificate); | |
1298 } | |
1299 | |
1300 | |
1301 int AlpnCallback(SSL *ssl, | |
1302 const uint8_t **out, | |
1303 uint8_t *outlen, | |
1304 const uint8_t *in, | |
1305 unsigned int inlen, | |
1306 void *arg) { | |
1307 // 'in' and 'arg' are sequences of (length, data) strings with 1-byte lengths. | |
1308 // 'arg' is 0-terminated. Finds the first string in 'arg' that is in 'in'. | |
1309 uint8_t* server_list = static_cast<uint8_t*>(arg); | |
1310 while (*server_list != 0) { | |
1311 uint8_t protocol_length = *server_list++; | |
1312 const uint8_t* client_list = in; | |
1313 while (client_list < in + inlen) { | |
1314 uint8_t client_protocol_length = *client_list++; | |
1315 if (client_protocol_length == protocol_length) { | |
1316 if (0 == memcmp(server_list, client_list, protocol_length)) { | |
1317 *out = client_list; | |
1318 *outlen = client_protocol_length; | |
1319 return SSL_TLSEXT_ERR_OK; // Success | |
1320 } | |
1321 } | |
1322 client_list += client_protocol_length; | |
1323 } | |
1324 server_list += protocol_length; | |
1325 } | |
1326 // TODO(23580): Make failure send a fatal alert instead of ignoring ALPN. | |
1327 return SSL_TLSEXT_ERR_NOACK; | |
1328 } | |
1329 | |
1330 | |
1331 // Sets the protocol list for ALPN on a SSL object or a context. | |
1332 static void SetAlpnProtocolList(Dart_Handle protocols_handle, | |
1333 SSL* ssl, | |
1334 SSL_CTX* context, | |
1335 bool is_server) { | |
1336 // Enable ALPN (application layer protocol negotiation) if the caller provides | |
1337 // a valid list of supported protocols. | |
1338 Dart_TypedData_Type protocols_type; | |
1339 uint8_t* protocol_string = NULL; | |
1340 uint8_t* protocol_string_copy = NULL; | |
1341 intptr_t protocol_string_len = 0; | |
1342 int status; | |
1343 | |
1344 Dart_Handle result = Dart_TypedDataAcquireData( | |
1345 protocols_handle, | |
1346 &protocols_type, | |
1347 reinterpret_cast<void**>(&protocol_string), | |
1348 &protocol_string_len); | |
1349 if (Dart_IsError(result)) { | |
1350 Dart_PropagateError(result); | |
1351 } | |
1352 | |
1353 if (protocols_type != Dart_TypedData_kUint8) { | |
1354 Dart_TypedDataReleaseData(protocols_handle); | |
1355 Dart_PropagateError(Dart_NewApiError( | |
1356 "Unexpected type for protocols (expected valid Uint8List).")); | |
1357 } | |
1358 | |
1359 if (protocol_string_len > 0) { | |
1360 if (is_server) { | |
1361 // ALPN on server connections must be set on an SSL_CTX object, | |
1362 // not on the SSL object of the individual connection. | |
1363 ASSERT(context != NULL); | |
1364 ASSERT(ssl == NULL); | |
1365 // Because it must be passed as a single void*, terminate | |
1366 // the list of (length, data) strings with a length 0 string. | |
1367 protocol_string_copy = | |
1368 static_cast<uint8_t*>(malloc(protocol_string_len + 1)); | |
1369 memmove(protocol_string_copy, protocol_string, protocol_string_len); | |
1370 protocol_string_copy[protocol_string_len] = '\0'; | |
1371 SSL_CTX_set_alpn_select_cb(context, AlpnCallback, protocol_string_copy); | |
1372 // TODO(whesse): If this function is called again, free the previous | |
1373 // protocol_string_copy. It may be better to keep this as a native | |
1374 // field on the Dart object, since fetching it from the structure is | |
1375 // not in the public api. | |
1376 // Also free protocol_string_copy when the context is destroyed, | |
1377 // in FreeSecurityContext() | |
1378 } else { | |
1379 // The function makes a local copy of protocol_string, which it owns. | |
1380 if (ssl != NULL) { | |
1381 ASSERT(context == NULL); | |
1382 status = SSL_set_alpn_protos(ssl, protocol_string, protocol_string_len); | |
1383 } else { | |
1384 ASSERT(context != NULL); | |
1385 ASSERT(ssl == NULL); | |
1386 status = SSL_CTX_set_alpn_protos( | |
1387 context, protocol_string, protocol_string_len); | |
1388 } | |
1389 ASSERT(status == 0); // The function returns a non-standard status. | |
1390 } | |
1391 } | |
1392 Dart_TypedDataReleaseData(protocols_handle); | |
1393 } | |
1394 | |
1395 | |
1396 void SSLFilter::Connect(const char* hostname, | |
1397 SSL_CTX* context, | |
1398 bool is_server, | |
1399 bool request_client_certificate, | |
1400 bool require_client_certificate, | |
1401 Dart_Handle protocols_handle) { | |
1402 is_server_ = is_server; | |
1403 if (in_handshake_) { | |
1404 FATAL("Connect called twice on the same _SecureFilter."); | |
1405 } | |
1406 | |
1407 int status; | |
1408 int error; | |
1409 BIO* ssl_side; | |
1410 status = BIO_new_bio_pair(&ssl_side, 10000, &socket_side_, 10000); | |
1411 CheckStatus(status, "TlsException", "BIO_new_bio_pair"); | |
1412 | |
1413 assert(context != NULL); | |
1414 ssl_ = SSL_new(context); | |
1415 SSL_set_bio(ssl_, ssl_side, ssl_side); | |
1416 SSL_set_mode(ssl_, SSL_MODE_AUTO_RETRY); // TODO(whesse): Is this right? | |
1417 SSL_set_ex_data(ssl_, filter_ssl_index, this); | |
1418 | |
1419 if (is_server_) { | |
1420 int certificate_mode = | |
1421 request_client_certificate ? SSL_VERIFY_PEER : SSL_VERIFY_NONE; | |
1422 if (require_client_certificate) { | |
1423 certificate_mode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT; | |
1424 } | |
1425 SSL_set_verify(ssl_, certificate_mode, NULL); | |
1426 } else { | |
1427 SetAlpnProtocolList(protocols_handle, ssl_, NULL, false); | |
1428 status = SSL_set_tlsext_host_name(ssl_, hostname); | |
1429 CheckStatus(status, "TlsException", "Set SNI host name"); | |
1430 // Sets the hostname in the certificate-checking object, so it is checked | |
1431 // against the certificate presented by the server. | |
1432 X509_VERIFY_PARAM* certificate_checking_parameters = SSL_get0_param(ssl_); | |
1433 hostname_ = strdup(hostname); | |
1434 X509_VERIFY_PARAM_set_flags(certificate_checking_parameters, | |
1435 X509_V_FLAG_PARTIAL_CHAIN | | |
1436 X509_V_FLAG_TRUSTED_FIRST); | |
1437 X509_VERIFY_PARAM_set_hostflags(certificate_checking_parameters, 0); | |
1438 status = X509_VERIFY_PARAM_set1_host(certificate_checking_parameters, | |
1439 hostname_, strlen(hostname_)); | |
1440 CheckStatus(status, "TlsException", | |
1441 "Set hostname for certificate checking"); | |
1442 } | |
1443 // Make the connection: | |
1444 if (is_server_) { | |
1445 status = SSL_accept(ssl_); | |
1446 if (SSL_LOG_STATUS) Log::Print("SSL_accept status: %d\n", status); | |
1447 if (status != 1) { | |
1448 // TODO(whesse): expect a needs-data error here. Handle other errors. | |
1449 error = SSL_get_error(ssl_, status); | |
1450 if (SSL_LOG_STATUS) Log::Print("SSL_accept error: %d\n", error); | |
1451 } | |
1452 } else { | |
1453 status = SSL_connect(ssl_); | |
1454 if (SSL_LOG_STATUS) Log::Print("SSL_connect status: %d\n", status); | |
1455 if (status != 1) { | |
1456 // TODO(whesse): expect a needs-data error here. Handle other errors. | |
1457 error = SSL_get_error(ssl_, status); | |
1458 if (SSL_LOG_STATUS) Log::Print("SSL_connect error: %d\n", error); | |
1459 } | |
1460 } | |
1461 Handshake(); | |
1462 } | |
1463 | |
1464 | |
1465 int printErrorCallback(const char *str, size_t len, void *ctx) { | |
1466 Log::PrintErr("%.*s\n", static_cast<int>(len), str); | |
1467 return 1; | |
1468 } | |
1469 | |
1470 void SSLFilter::Handshake() { | |
1471 // Try and push handshake along. | |
1472 int status; | |
1473 status = SSL_do_handshake(ssl_); | |
1474 if (callback_error != NULL) { | |
1475 // The SSL_do_handshake will try performing a handshake and might call | |
1476 // a CertificateCallback. If the certificate validation | |
1477 // failed the 'callback_error" will be set by the certificateCallback | |
1478 // logic and we propagate the error" | |
1479 Dart_PropagateError(callback_error); | |
1480 } | |
1481 if (SSL_want_write(ssl_) || SSL_want_read(ssl_)) { | |
1482 in_handshake_ = true; | |
1483 return; | |
1484 } | |
1485 CheckStatus(status, | |
1486 "HandshakeException", | |
1487 is_server_ ? "Handshake error in server" : "Handshake error in client"); | |
1488 // Handshake succeeded. | |
1489 if (in_handshake_) { | |
1490 // TODO(24071): Check return value of SSL_get_verify_result, this | |
1491 // should give us the hostname check. | |
1492 int result = SSL_get_verify_result(ssl_); | |
1493 if (SSL_LOG_STATUS) { | |
1494 Log::Print("Handshake verification status: %d\n", result); | |
1495 X509* peer_certificate = SSL_get_peer_certificate(ssl_); | |
1496 if (peer_certificate == NULL) { | |
1497 Log::Print("No peer certificate received\n"); | |
1498 } else { | |
1499 X509_NAME* s_name = X509_get_subject_name(peer_certificate); | |
1500 printf("Peer certificate SN: "); | |
1501 X509_NAME_print_ex_fp(stdout, s_name, 4, 0); | |
1502 printf("\n"); | |
1503 } | |
1504 } | |
1505 ThrowIfError(Dart_InvokeClosure( | |
1506 Dart_HandleFromPersistent(handshake_complete_), 0, NULL)); | |
1507 in_handshake_ = false; | |
1508 } | |
1509 } | |
1510 | |
1511 void SSLFilter::GetSelectedProtocol(Dart_NativeArguments args) { | |
1512 const uint8_t* protocol; | |
1513 unsigned length; | |
1514 SSL_get0_alpn_selected(ssl_, &protocol, &length); | |
1515 if (length == 0) { | |
1516 Dart_SetReturnValue(args, Dart_Null()); | |
1517 } else { | |
1518 Dart_SetReturnValue(args, Dart_NewStringFromUTF8(protocol, length)); | |
1519 } | |
1520 } | |
1521 | |
1522 | |
1523 void SSLFilter::Renegotiate(bool use_session_cache, | |
1524 bool request_client_certificate, | |
1525 bool require_client_certificate) { | |
1526 // The SSL_REQUIRE_CERTIFICATE option only takes effect if the | |
1527 // SSL_REQUEST_CERTIFICATE option is also set, so set it. | |
1528 request_client_certificate = | |
1529 request_client_certificate || require_client_certificate; | |
1530 // TODO(24070, 24069): Implement setting the client certificate parameters, | |
1531 // and triggering rehandshake. | |
1532 } | |
1533 | |
1534 | |
1535 SSLFilter::~SSLFilter() { | |
1536 if (ssl_ != NULL) { | |
1537 SSL_free(ssl_); | |
1538 ssl_ = NULL; | |
1539 } | |
1540 if (socket_side_ != NULL) { | |
1541 BIO_free(socket_side_); | |
1542 socket_side_ = NULL; | |
1543 } | |
1544 if (hostname_ != NULL) { | |
1545 free(hostname_); | |
1546 hostname_ = NULL; | |
1547 } | |
1548 for (int i = 0; i < kNumBuffers; ++i) { | |
1549 if (buffers_[i] != NULL) { | |
1550 delete[] buffers_[i]; | |
1551 buffers_[i] = NULL; | |
1552 } | |
1553 } | |
1554 } | |
1555 | |
1556 | |
1557 void SSLFilter::Destroy() { | |
1558 for (int i = 0; i < kNumBuffers; ++i) { | |
1559 if (dart_buffer_objects_[i] != NULL) { | |
1560 Dart_DeletePersistentHandle(dart_buffer_objects_[i]); | |
1561 dart_buffer_objects_[i] = NULL; | |
1562 } | |
1563 } | |
1564 if (string_start_ != NULL) { | |
1565 Dart_DeletePersistentHandle(string_start_); | |
1566 string_start_ = NULL; | |
1567 } | |
1568 if (string_length_ != NULL) { | |
1569 Dart_DeletePersistentHandle(string_length_); | |
1570 string_length_ = NULL; | |
1571 } | |
1572 if (handshake_complete_ != NULL) { | |
1573 Dart_DeletePersistentHandle(handshake_complete_); | |
1574 handshake_complete_ = NULL; | |
1575 } | |
1576 if (bad_certificate_callback_ != NULL) { | |
1577 Dart_DeletePersistentHandle(bad_certificate_callback_); | |
1578 bad_certificate_callback_ = NULL; | |
1579 } | |
1580 } | |
1581 | |
1582 | |
1583 /* Read decrypted data from the filter to the circular buffer */ | |
1584 int SSLFilter::ProcessReadPlaintextBuffer(int start, int end) { | |
1585 int length = end - start; | |
1586 int bytes_processed = 0; | |
1587 if (length > 0) { | |
1588 bytes_processed = SSL_read( | |
1589 ssl_, | |
1590 reinterpret_cast<char*>((buffers_[kReadPlaintext] + start)), | |
1591 length); | |
1592 if (bytes_processed < 0) { | |
1593 int error = SSL_get_error(ssl_, bytes_processed); | |
1594 USE(error); | |
1595 bytes_processed = 0; | |
1596 } | |
1597 } | |
1598 return bytes_processed; | |
1599 } | |
1600 | |
1601 | |
1602 int SSLFilter::ProcessWritePlaintextBuffer(int start, int end) { | |
1603 int length = end - start; | |
1604 int bytes_processed = SSL_write( | |
1605 ssl_, buffers_[kWritePlaintext] + start, length); | |
1606 if (bytes_processed < 0) { | |
1607 if (SSL_LOG_DATA) { | |
1608 Log::Print("SSL_write returned error %d\n", bytes_processed); | |
1609 } | |
1610 return 0; | |
1611 } | |
1612 return bytes_processed; | |
1613 } | |
1614 | |
1615 | |
1616 /* Read encrypted data from the circular buffer to the filter */ | |
1617 int SSLFilter::ProcessReadEncryptedBuffer(int start, int end) { | |
1618 int length = end - start; | |
1619 if (SSL_LOG_DATA) Log::Print( | |
1620 "Entering ProcessReadEncryptedBuffer with %d bytes\n", length); | |
1621 int bytes_processed = 0; | |
1622 if (length > 0) { | |
1623 bytes_processed = | |
1624 BIO_write(socket_side_, buffers_[kReadEncrypted] + start, length); | |
1625 if (bytes_processed <= 0) { | |
1626 bool retry = BIO_should_retry(socket_side_); | |
1627 if (!retry) { | |
1628 if (SSL_LOG_DATA) Log::Print( | |
1629 "BIO_write failed in ReadEncryptedBuffer\n"); | |
1630 } | |
1631 bytes_processed = 0; | |
1632 } | |
1633 } | |
1634 if (SSL_LOG_DATA) Log::Print( | |
1635 "Leaving ProcessReadEncryptedBuffer wrote %d bytes\n", bytes_processed); | |
1636 return bytes_processed; | |
1637 } | |
1638 | |
1639 | |
1640 int SSLFilter::ProcessWriteEncryptedBuffer(int start, int end) { | |
1641 int length = end - start; | |
1642 int bytes_processed = 0; | |
1643 if (length > 0) { | |
1644 bytes_processed = BIO_read(socket_side_, | |
1645 buffers_[kWriteEncrypted] + start, | |
1646 length); | |
1647 if (bytes_processed < 0) { | |
1648 if (SSL_LOG_DATA) Log::Print( | |
1649 "WriteEncrypted BIO_read returned error %d\n", bytes_processed); | |
1650 return 0; | |
1651 } else { | |
1652 if (SSL_LOG_DATA) Log::Print( | |
1653 "WriteEncrypted BIO_read wrote %d bytes\n", bytes_processed); | |
1654 } | |
1655 } | |
1656 return bytes_processed; | |
1657 } | |
1658 | |
1659 } // namespace bin | |
1660 } // namespace dart | |
OLD | NEW |