1 | // Copyright (c) 2017, 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 | #if !defined(DART_IO_SECURE_SOCKET_DISABLED) |
6 | |
7 | #include "bin/secure_socket_filter.h" |
8 | |
9 | #include <openssl/bio.h> |
10 | #include <openssl/ssl.h> |
11 | #include <openssl/x509.h> |
12 | |
13 | #include "bin/lockers.h" |
14 | #include "bin/secure_socket_utils.h" |
15 | #include "bin/security_context.h" |
16 | #include "platform/syslog.h" |
17 | #include "platform/text_buffer.h" |
18 | |
19 | // Return the error from the containing function if handle is an error handle. |
20 | #define RETURN_IF_ERROR(handle) \ |
21 | { \ |
22 | Dart_Handle __handle = handle; \ |
23 | if (Dart_IsError((__handle))) { \ |
24 | return __handle; \ |
25 | } \ |
26 | } |
27 | |
28 | namespace dart { |
29 | namespace bin { |
30 | |
31 | bool SSLFilter::library_initialized_ = false; |
32 | // To protect library initialization. |
33 | Mutex* SSLFilter::mutex_ = nullptr; |
34 | int SSLFilter::filter_ssl_index; |
35 | |
36 | void SSLFilter::Init() { |
37 | ASSERT(SSLFilter::mutex_ == nullptr); |
38 | SSLFilter::mutex_ = new Mutex(); |
39 | } |
40 | |
41 | void SSLFilter::Cleanup() { |
42 | ASSERT(SSLFilter::mutex_ != nullptr); |
43 | delete SSLFilter::mutex_; |
44 | SSLFilter::mutex_ = nullptr; |
45 | } |
46 | |
47 | const intptr_t SSLFilter::kInternalBIOSize = 10 * KB; |
48 | const intptr_t SSLFilter::kApproximateSize = |
49 | sizeof(SSLFilter) + (2 * SSLFilter::kInternalBIOSize); |
50 | |
51 | static SSLFilter* GetFilter(Dart_NativeArguments args) { |
52 | SSLFilter* filter = NULL; |
53 | Dart_Handle dart_this = ThrowIfError(Dart_GetNativeArgument(args, 0)); |
54 | ASSERT(Dart_IsInstance(dart_this)); |
55 | ThrowIfError(Dart_GetNativeInstanceField( |
56 | dart_this, SSLFilter::kSSLFilterNativeFieldIndex, |
57 | reinterpret_cast<intptr_t*>(&filter))); |
58 | if (filter == NULL) { |
59 | Dart_PropagateError(Dart_NewUnhandledExceptionError( |
60 | DartUtils::NewInternalError("No native peer" ))); |
61 | } |
62 | return filter; |
63 | } |
64 | |
65 | static void DeleteFilter(void* isolate_data, void* context_pointer) { |
66 | SSLFilter* filter = reinterpret_cast<SSLFilter*>(context_pointer); |
67 | filter->Release(); |
68 | } |
69 | |
70 | static Dart_Handle SetFilter(Dart_NativeArguments args, SSLFilter* filter) { |
71 | ASSERT(filter != NULL); |
72 | Dart_Handle dart_this = Dart_GetNativeArgument(args, 0); |
73 | RETURN_IF_ERROR(dart_this); |
74 | ASSERT(Dart_IsInstance(dart_this)); |
75 | Dart_Handle err = Dart_SetNativeInstanceField( |
76 | dart_this, SSLFilter::kSSLFilterNativeFieldIndex, |
77 | reinterpret_cast<intptr_t>(filter)); |
78 | RETURN_IF_ERROR(err); |
79 | Dart_NewFinalizableHandle(dart_this, reinterpret_cast<void*>(filter), |
80 | SSLFilter::kApproximateSize, DeleteFilter); |
81 | return Dart_Null(); |
82 | } |
83 | |
84 | void FUNCTION_NAME(SecureSocket_Init)(Dart_NativeArguments args) { |
85 | Dart_Handle dart_this = ThrowIfError(Dart_GetNativeArgument(args, 0)); |
86 | SSLFilter* filter = new SSLFilter(); |
87 | Dart_Handle err = SetFilter(args, filter); |
88 | if (Dart_IsError(err)) { |
89 | filter->Release(); |
90 | Dart_PropagateError(err); |
91 | } |
92 | err = filter->Init(dart_this); |
93 | if (Dart_IsError(err)) { |
94 | // The finalizer was set up by SetFilter. It will delete `filter` if there |
95 | // is an error. |
96 | filter->Destroy(); |
97 | Dart_PropagateError(err); |
98 | } |
99 | } |
100 | |
101 | void FUNCTION_NAME(SecureSocket_Connect)(Dart_NativeArguments args) { |
102 | Dart_Handle host_name_object = ThrowIfError(Dart_GetNativeArgument(args, 1)); |
103 | Dart_Handle context_object = ThrowIfError(Dart_GetNativeArgument(args, 2)); |
104 | bool is_server = DartUtils::GetBooleanValue(Dart_GetNativeArgument(args, 3)); |
105 | bool request_client_certificate = |
106 | DartUtils::GetBooleanValue(Dart_GetNativeArgument(args, 4)); |
107 | bool require_client_certificate = |
108 | DartUtils::GetBooleanValue(Dart_GetNativeArgument(args, 5)); |
109 | Dart_Handle protocols_handle = ThrowIfError(Dart_GetNativeArgument(args, 6)); |
110 | |
111 | const char* host_name = NULL; |
112 | // TODO(whesse): Is truncating a Dart string containing \0 what we want? |
113 | ThrowIfError(Dart_StringToCString(host_name_object, &host_name)); |
114 | |
115 | SSLCertContext* context = NULL; |
116 | if (!Dart_IsNull(context_object)) { |
117 | ThrowIfError(Dart_GetNativeInstanceField( |
118 | context_object, SSLCertContext::kSecurityContextNativeFieldIndex, |
119 | reinterpret_cast<intptr_t*>(&context))); |
120 | } |
121 | |
122 | // The protocols_handle is guaranteed to be a valid Uint8List. |
123 | // It will have the correct length encoding of the protocols array. |
124 | ASSERT(!Dart_IsNull(protocols_handle)); |
125 | GetFilter(args)->Connect(host_name, context, is_server, |
126 | request_client_certificate, |
127 | require_client_certificate, protocols_handle); |
128 | } |
129 | |
130 | void FUNCTION_NAME(SecureSocket_Destroy)(Dart_NativeArguments args) { |
131 | SSLFilter* filter = GetFilter(args); |
132 | // There are two paths that can clean up an SSLFilter object. First, |
133 | // there is this explicit call to Destroy(), called from |
134 | // _SecureFilter.destroy() in Dart code. After a call to destroy(), the Dart |
135 | // code maintains the invariant that there will be no futher SSLFilter |
136 | // requests sent to the IO Service. Therefore, the internals of the SSLFilter |
137 | // are safe to deallocate, but not the SSLFilter itself, which is already |
138 | // set up to be cleaned up by the finalizer. |
139 | // |
140 | // The second path is through the finalizer, which we have to do in case |
141 | // some mishap prevents a call to _SecureFilter.destroy(). |
142 | filter->Destroy(); |
143 | } |
144 | |
145 | void FUNCTION_NAME(SecureSocket_Handshake)(Dart_NativeArguments args) { |
146 | GetFilter(args)->Handshake(); |
147 | } |
148 | |
149 | void FUNCTION_NAME(SecureSocket_GetSelectedProtocol)( |
150 | Dart_NativeArguments args) { |
151 | GetFilter(args)->GetSelectedProtocol(args); |
152 | } |
153 | |
154 | void FUNCTION_NAME(SecureSocket_Renegotiate)(Dart_NativeArguments args) { |
155 | bool use_session_cache = |
156 | DartUtils::GetBooleanValue(Dart_GetNativeArgument(args, 1)); |
157 | bool request_client_certificate = |
158 | DartUtils::GetBooleanValue(Dart_GetNativeArgument(args, 2)); |
159 | bool require_client_certificate = |
160 | DartUtils::GetBooleanValue(Dart_GetNativeArgument(args, 3)); |
161 | GetFilter(args)->Renegotiate(use_session_cache, request_client_certificate, |
162 | require_client_certificate); |
163 | } |
164 | |
165 | void FUNCTION_NAME(SecureSocket_RegisterHandshakeCompleteCallback)( |
166 | Dart_NativeArguments args) { |
167 | Dart_Handle handshake_complete = |
168 | ThrowIfError(Dart_GetNativeArgument(args, 1)); |
169 | if (!Dart_IsClosure(handshake_complete)) { |
170 | Dart_ThrowException(DartUtils::NewDartArgumentError( |
171 | "Illegal argument to RegisterHandshakeCompleteCallback" )); |
172 | } |
173 | GetFilter(args)->RegisterHandshakeCompleteCallback(handshake_complete); |
174 | } |
175 | |
176 | void FUNCTION_NAME(SecureSocket_RegisterBadCertificateCallback)( |
177 | Dart_NativeArguments args) { |
178 | Dart_Handle callback = ThrowIfError(Dart_GetNativeArgument(args, 1)); |
179 | if (!Dart_IsClosure(callback) && !Dart_IsNull(callback)) { |
180 | Dart_ThrowException(DartUtils::NewDartArgumentError( |
181 | "Illegal argument to RegisterBadCertificateCallback" )); |
182 | } |
183 | GetFilter(args)->RegisterBadCertificateCallback(callback); |
184 | } |
185 | |
186 | void FUNCTION_NAME(SecureSocket_PeerCertificate)(Dart_NativeArguments args) { |
187 | Dart_Handle cert = ThrowIfError(GetFilter(args)->PeerCertificate()); |
188 | Dart_SetReturnValue(args, cert); |
189 | } |
190 | |
191 | void FUNCTION_NAME(SecureSocket_FilterPointer)(Dart_NativeArguments args) { |
192 | SSLFilter* filter = GetFilter(args); |
193 | // This filter pointer is passed to the IO Service thread. The IO Service |
194 | // thread must Release() the pointer when it is done with it. |
195 | filter->Retain(); |
196 | intptr_t filter_pointer = reinterpret_cast<intptr_t>(filter); |
197 | Dart_SetReturnValue(args, Dart_NewInteger(filter_pointer)); |
198 | } |
199 | |
200 | /** |
201 | * Pushes data through the SSL filter, reading and writing from circular |
202 | * buffers shared with Dart. |
203 | * |
204 | * The Dart _SecureFilterImpl class contains 4 ExternalByteArrays used to |
205 | * pass encrypted and plaintext data to and from the C++ SSLFilter object. |
206 | * |
207 | * ProcessFilter is called with a CObject array containing the pointer to |
208 | * the SSLFilter, encoded as an int, and the start and end positions of the |
209 | * valid data in the four circular buffers. The function only reads from |
210 | * the valid data area of the input buffers, and only writes to the free |
211 | * area of the output buffers. The function returns the new start and end |
212 | * positions in the buffers, but it only updates start for input buffers, and |
213 | * end for output buffers. Therefore, the Dart thread can simultaneously |
214 | * write to the free space and end pointer of input buffers, and read from |
215 | * the data space of output buffers, and modify the start pointer. |
216 | * |
217 | * When ProcessFilter returns, the Dart thread is responsible for combining |
218 | * the updated pointers from Dart and C++, to make the new valid state of |
219 | * the circular buffer. |
220 | */ |
221 | CObject* SSLFilter::ProcessFilterRequest(const CObjectArray& request) { |
222 | CObjectIntptr filter_object(request[0]); |
223 | SSLFilter* filter = reinterpret_cast<SSLFilter*>(filter_object.Value()); |
224 | RefCntReleaseScope<SSLFilter> rs(filter); |
225 | |
226 | bool in_handshake = CObjectBool(request[1]).Value(); |
227 | int starts[SSLFilter::kNumBuffers]; |
228 | int ends[SSLFilter::kNumBuffers]; |
229 | for (int i = 0; i < SSLFilter::kNumBuffers; ++i) { |
230 | starts[i] = CObjectInt32(request[2 * i + 2]).Value(); |
231 | ends[i] = CObjectInt32(request[2 * i + 3]).Value(); |
232 | } |
233 | |
234 | if (filter->ProcessAllBuffers(starts, ends, in_handshake)) { |
235 | CObjectArray* result = |
236 | new CObjectArray(CObject::NewArray(SSLFilter::kNumBuffers * 2)); |
237 | for (int i = 0; i < SSLFilter::kNumBuffers; ++i) { |
238 | result->SetAt(2 * i, new CObjectInt32(CObject::NewInt32(starts[i]))); |
239 | result->SetAt(2 * i + 1, new CObjectInt32(CObject::NewInt32(ends[i]))); |
240 | } |
241 | return result; |
242 | } else { |
243 | int32_t error_code = static_cast<int32_t>(ERR_peek_error()); |
244 | TextBuffer error_string(SecureSocketUtils::SSL_ERROR_MESSAGE_BUFFER_SIZE); |
245 | SecureSocketUtils::FetchErrorString(filter->ssl_, &error_string); |
246 | CObjectArray* result = new CObjectArray(CObject::NewArray(2)); |
247 | result->SetAt(0, new CObjectInt32(CObject::NewInt32(error_code))); |
248 | result->SetAt(1, |
249 | new CObjectString(CObject::NewString(error_string.buffer()))); |
250 | return result; |
251 | } |
252 | } |
253 | |
254 | bool SSLFilter::ProcessAllBuffers(int starts[kNumBuffers], |
255 | int ends[kNumBuffers], |
256 | bool in_handshake) { |
257 | for (int i = 0; i < kNumBuffers; ++i) { |
258 | if (in_handshake && (i == kReadPlaintext || i == kWritePlaintext)) continue; |
259 | int start = starts[i]; |
260 | int end = ends[i]; |
261 | int size = IsBufferEncrypted(i) ? encrypted_buffer_size_ : buffer_size_; |
262 | if (start < 0 || end < 0 || start >= size || end >= size) { |
263 | FATAL("Out-of-bounds internal buffer access in dart:io SecureSocket" ); |
264 | } |
265 | switch (i) { |
266 | case kReadPlaintext: |
267 | case kWriteEncrypted: |
268 | // Write data to the circular buffer's free space. If the buffer |
269 | // is full, neither if statement is executed and nothing happens. |
270 | if (start <= end) { |
271 | // If the free space may be split into two segments, |
272 | // then the first is [end, size), unless start == 0. |
273 | // Then, since the last free byte is at position start - 2, |
274 | // the interval is [end, size - 1). |
275 | int buffer_end = (start == 0) ? size - 1 : size; |
276 | int bytes = (i == kReadPlaintext) |
277 | ? ProcessReadPlaintextBuffer(end, buffer_end) |
278 | : ProcessWriteEncryptedBuffer(end, buffer_end); |
279 | if (bytes < 0) return false; |
280 | end += bytes; |
281 | ASSERT(end <= size); |
282 | if (end == size) end = 0; |
283 | } |
284 | if (start > end + 1) { |
285 | int bytes = (i == kReadPlaintext) |
286 | ? ProcessReadPlaintextBuffer(end, start - 1) |
287 | : ProcessWriteEncryptedBuffer(end, start - 1); |
288 | if (bytes < 0) return false; |
289 | end += bytes; |
290 | ASSERT(end < start); |
291 | } |
292 | ends[i] = end; |
293 | break; |
294 | case kReadEncrypted: |
295 | case kWritePlaintext: |
296 | // Read/Write data from circular buffer. If the buffer is empty, |
297 | // neither if statement's condition is true. |
298 | if (end < start) { |
299 | // Data may be split into two segments. In this case, |
300 | // the first is [start, size). |
301 | int bytes = (i == kReadEncrypted) |
302 | ? ProcessReadEncryptedBuffer(start, size) |
303 | : ProcessWritePlaintextBuffer(start, size); |
304 | if (bytes < 0) return false; |
305 | start += bytes; |
306 | ASSERT(start <= size); |
307 | if (start == size) start = 0; |
308 | } |
309 | if (start < end) { |
310 | int bytes = (i == kReadEncrypted) |
311 | ? ProcessReadEncryptedBuffer(start, end) |
312 | : ProcessWritePlaintextBuffer(start, end); |
313 | if (bytes < 0) return false; |
314 | start += bytes; |
315 | ASSERT(start <= end); |
316 | } |
317 | starts[i] = start; |
318 | break; |
319 | default: |
320 | UNREACHABLE(); |
321 | } |
322 | } |
323 | return true; |
324 | } |
325 | |
326 | Dart_Handle SSLFilter::Init(Dart_Handle dart_this) { |
327 | if (!library_initialized_) { |
328 | InitializeLibrary(); |
329 | } |
330 | ASSERT(string_start_ == NULL); |
331 | string_start_ = Dart_NewPersistentHandle(DartUtils::NewString("start" )); |
332 | ASSERT(string_start_ != NULL); |
333 | ASSERT(string_length_ == NULL); |
334 | string_length_ = Dart_NewPersistentHandle(DartUtils::NewString("length" )); |
335 | ASSERT(string_length_ != NULL); |
336 | ASSERT(bad_certificate_callback_ == NULL); |
337 | bad_certificate_callback_ = Dart_NewPersistentHandle(Dart_Null()); |
338 | ASSERT(bad_certificate_callback_ != NULL); |
339 | // Caller handles cleanup on an error. |
340 | return InitializeBuffers(dart_this); |
341 | } |
342 | |
343 | Dart_Handle SSLFilter::InitializeBuffers(Dart_Handle dart_this) { |
344 | // Create SSLFilter buffers as ExternalUint8Array objects. |
345 | Dart_Handle buffers_string = DartUtils::NewString("buffers" ); |
346 | RETURN_IF_ERROR(buffers_string); |
347 | Dart_Handle dart_buffers_object = Dart_GetField(dart_this, buffers_string); |
348 | RETURN_IF_ERROR(dart_buffers_object); |
349 | Dart_Handle secure_filter_impl_type = Dart_InstanceGetType(dart_this); |
350 | RETURN_IF_ERROR(secure_filter_impl_type); |
351 | Dart_Handle size_string = DartUtils::NewString("SIZE" ); |
352 | RETURN_IF_ERROR(size_string); |
353 | Dart_Handle dart_buffer_size = |
354 | Dart_GetField(secure_filter_impl_type, size_string); |
355 | RETURN_IF_ERROR(dart_buffer_size); |
356 | |
357 | int64_t buffer_size = 0; |
358 | Dart_Handle err = Dart_IntegerToInt64(dart_buffer_size, &buffer_size); |
359 | RETURN_IF_ERROR(err); |
360 | |
361 | Dart_Handle encrypted_size_string = DartUtils::NewString("ENCRYPTED_SIZE" ); |
362 | RETURN_IF_ERROR(encrypted_size_string); |
363 | |
364 | Dart_Handle dart_encrypted_buffer_size = |
365 | Dart_GetField(secure_filter_impl_type, encrypted_size_string); |
366 | RETURN_IF_ERROR(dart_encrypted_buffer_size); |
367 | |
368 | int64_t encrypted_buffer_size = 0; |
369 | err = Dart_IntegerToInt64(dart_encrypted_buffer_size, &encrypted_buffer_size); |
370 | RETURN_IF_ERROR(err); |
371 | |
372 | if (buffer_size <= 0 || buffer_size > 1 * MB) { |
373 | FATAL("Invalid buffer size in _ExternalBuffer" ); |
374 | } |
375 | if (encrypted_buffer_size <= 0 || encrypted_buffer_size > 1 * MB) { |
376 | FATAL("Invalid encrypted buffer size in _ExternalBuffer" ); |
377 | } |
378 | buffer_size_ = static_cast<int>(buffer_size); |
379 | encrypted_buffer_size_ = static_cast<int>(encrypted_buffer_size); |
380 | |
381 | Dart_Handle data_identifier = DartUtils::NewString("data" ); |
382 | RETURN_IF_ERROR(data_identifier); |
383 | |
384 | for (int i = 0; i < kNumBuffers; i++) { |
385 | int size = IsBufferEncrypted(i) ? encrypted_buffer_size_ : buffer_size_; |
386 | buffers_[i] = new uint8_t[size]; |
387 | ASSERT(buffers_[i] != NULL); |
388 | memset(buffers_[i], 0, size); |
389 | dart_buffer_objects_[i] = NULL; |
390 | } |
391 | |
392 | Dart_Handle result = Dart_Null(); |
393 | for (int i = 0; i < kNumBuffers; ++i) { |
394 | int size = IsBufferEncrypted(i) ? encrypted_buffer_size_ : buffer_size_; |
395 | result = Dart_ListGetAt(dart_buffers_object, i); |
396 | if (Dart_IsError(result)) { |
397 | break; |
398 | } |
399 | |
400 | dart_buffer_objects_[i] = Dart_NewPersistentHandle(result); |
401 | ASSERT(dart_buffer_objects_[i] != NULL); |
402 | Dart_Handle data = |
403 | Dart_NewExternalTypedData(Dart_TypedData_kUint8, buffers_[i], size); |
404 | if (Dart_IsError(data)) { |
405 | result = data; |
406 | break; |
407 | } |
408 | result = Dart_HandleFromPersistent(dart_buffer_objects_[i]); |
409 | if (Dart_IsError(result)) { |
410 | break; |
411 | } |
412 | result = Dart_SetField(result, data_identifier, data); |
413 | if (Dart_IsError(result)) { |
414 | break; |
415 | } |
416 | } |
417 | |
418 | // Caller handles cleanup on an error. |
419 | return result; |
420 | } |
421 | |
422 | void SSLFilter::RegisterHandshakeCompleteCallback(Dart_Handle complete) { |
423 | ASSERT(NULL == handshake_complete_); |
424 | handshake_complete_ = Dart_NewPersistentHandle(complete); |
425 | |
426 | ASSERT(handshake_complete_ != NULL); |
427 | } |
428 | |
429 | void SSLFilter::RegisterBadCertificateCallback(Dart_Handle callback) { |
430 | ASSERT(bad_certificate_callback_ != NULL); |
431 | Dart_DeletePersistentHandle(bad_certificate_callback_); |
432 | bad_certificate_callback_ = Dart_NewPersistentHandle(callback); |
433 | ASSERT(bad_certificate_callback_ != NULL); |
434 | } |
435 | |
436 | Dart_Handle SSLFilter::PeerCertificate() { |
437 | X509* ca = SSL_get_peer_certificate(ssl_); |
438 | if (ca == NULL) { |
439 | return Dart_Null(); |
440 | } |
441 | return X509Helper::WrappedX509Certificate(ca); |
442 | } |
443 | |
444 | void SSLFilter::InitializeLibrary() { |
445 | MutexLocker locker(mutex_); |
446 | if (!library_initialized_) { |
447 | SSL_library_init(); |
448 | filter_ssl_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL); |
449 | ASSERT(filter_ssl_index >= 0); |
450 | library_initialized_ = true; |
451 | } |
452 | } |
453 | |
454 | void SSLFilter::Connect(const char* hostname, |
455 | SSLCertContext* context, |
456 | bool is_server, |
457 | bool request_client_certificate, |
458 | bool require_client_certificate, |
459 | Dart_Handle protocols_handle) { |
460 | is_server_ = is_server; |
461 | if (in_handshake_) { |
462 | FATAL("Connect called twice on the same _SecureFilter." ); |
463 | } |
464 | |
465 | int status; |
466 | int error; |
467 | BIO* ssl_side; |
468 | status = BIO_new_bio_pair(&ssl_side, kInternalBIOSize, &socket_side_, |
469 | kInternalBIOSize); |
470 | SecureSocketUtils::CheckStatusSSL(status, "TlsException" , "BIO_new_bio_pair" , |
471 | ssl_); |
472 | |
473 | ASSERT(context != NULL); |
474 | ASSERT(context->context() != NULL); |
475 | ssl_ = SSL_new(context->context()); |
476 | SSL_set_bio(ssl_, ssl_side, ssl_side); |
477 | SSL_set_mode(ssl_, SSL_MODE_AUTO_RETRY); // TODO(whesse): Is this right? |
478 | SSL_set_ex_data(ssl_, filter_ssl_index, this); |
479 | context->RegisterCallbacks(ssl_); |
480 | |
481 | if (is_server_) { |
482 | int certificate_mode = |
483 | request_client_certificate ? SSL_VERIFY_PEER : SSL_VERIFY_NONE; |
484 | if (require_client_certificate) { |
485 | certificate_mode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT; |
486 | } |
487 | SSL_set_verify(ssl_, certificate_mode, NULL); |
488 | } else { |
489 | SSLCertContext::SetAlpnProtocolList(protocols_handle, ssl_, NULL, false); |
490 | status = SSL_set_tlsext_host_name(ssl_, hostname); |
491 | SecureSocketUtils::CheckStatusSSL(status, "TlsException" , |
492 | "Set SNI host name" , ssl_); |
493 | // Sets the hostname in the certificate-checking object, so it is checked |
494 | // against the certificate presented by the server. |
495 | X509_VERIFY_PARAM* certificate_checking_parameters = SSL_get0_param(ssl_); |
496 | hostname_ = Utils::StrDup(hostname); |
497 | X509_VERIFY_PARAM_set_flags( |
498 | certificate_checking_parameters, |
499 | X509_V_FLAG_PARTIAL_CHAIN | X509_V_FLAG_TRUSTED_FIRST); |
500 | X509_VERIFY_PARAM_set_hostflags(certificate_checking_parameters, 0); |
501 | status = X509_VERIFY_PARAM_set1_host(certificate_checking_parameters, |
502 | hostname_, strlen(hostname_)); |
503 | SecureSocketUtils::CheckStatusSSL( |
504 | status, "TlsException" , "Set hostname for certificate checking" , ssl_); |
505 | } |
506 | // Make the connection: |
507 | if (is_server_) { |
508 | status = SSL_accept(ssl_); |
509 | if (SSL_LOG_STATUS) { |
510 | Syslog::Print("SSL_accept status: %d\n" , status); |
511 | } |
512 | if (status != 1) { |
513 | // TODO(whesse): expect a needs-data error here. Handle other errors. |
514 | error = SSL_get_error(ssl_, status); |
515 | if (SSL_LOG_STATUS) { |
516 | Syslog::Print("SSL_accept error: %d\n" , error); |
517 | } |
518 | } |
519 | } else { |
520 | status = SSL_connect(ssl_); |
521 | if (SSL_LOG_STATUS) { |
522 | Syslog::Print("SSL_connect status: %d\n" , status); |
523 | } |
524 | if (status != 1) { |
525 | // TODO(whesse): expect a needs-data error here. Handle other errors. |
526 | error = SSL_get_error(ssl_, status); |
527 | if (SSL_LOG_STATUS) { |
528 | Syslog::Print("SSL_connect error: %d\n" , error); |
529 | } |
530 | } |
531 | } |
532 | Handshake(); |
533 | } |
534 | |
535 | void SSLFilter::Handshake() { |
536 | // Try and push handshake along. |
537 | int status; |
538 | status = SSL_do_handshake(ssl_); |
539 | if (callback_error != NULL) { |
540 | // The SSL_do_handshake will try performing a handshake and might call |
541 | // a CertificateCallback. If the certificate validation |
542 | // failed the 'callback_error" will be set by the certificateCallback |
543 | // logic and we propagate the error" |
544 | Dart_PropagateError(callback_error); |
545 | } |
546 | if (SSL_want_write(ssl_) || SSL_want_read(ssl_)) { |
547 | in_handshake_ = true; |
548 | return; |
549 | } |
550 | SecureSocketUtils::CheckStatusSSL( |
551 | status, "HandshakeException" , |
552 | is_server_ ? "Handshake error in server" : "Handshake error in client" , |
553 | ssl_); |
554 | // Handshake succeeded. |
555 | if (in_handshake_) { |
556 | // TODO(24071): Check return value of SSL_get_verify_result, this |
557 | // should give us the hostname check. |
558 | int result = SSL_get_verify_result(ssl_); |
559 | if (SSL_LOG_STATUS) { |
560 | Syslog::Print("Handshake verification status: %d\n" , result); |
561 | X509* peer_certificate = SSL_get_peer_certificate(ssl_); |
562 | if (peer_certificate == NULL) { |
563 | Syslog::Print("No peer certificate received\n" ); |
564 | } else { |
565 | X509_NAME* s_name = X509_get_subject_name(peer_certificate); |
566 | printf("Peer certificate SN: " ); |
567 | X509_NAME_print_ex_fp(stdout, s_name, 4, 0); |
568 | printf("\n" ); |
569 | } |
570 | } |
571 | ThrowIfError(Dart_InvokeClosure( |
572 | Dart_HandleFromPersistent(handshake_complete_), 0, NULL)); |
573 | in_handshake_ = false; |
574 | } |
575 | } |
576 | |
577 | void SSLFilter::GetSelectedProtocol(Dart_NativeArguments args) { |
578 | const uint8_t* protocol; |
579 | unsigned length; |
580 | SSL_get0_alpn_selected(ssl_, &protocol, &length); |
581 | if (length == 0) { |
582 | Dart_SetReturnValue(args, Dart_Null()); |
583 | } else { |
584 | Dart_SetReturnValue(args, Dart_NewStringFromUTF8(protocol, length)); |
585 | } |
586 | } |
587 | |
588 | void SSLFilter::Renegotiate(bool use_session_cache, |
589 | bool request_client_certificate, |
590 | bool require_client_certificate) { |
591 | // The SSL_REQUIRE_CERTIFICATE option only takes effect if the |
592 | // SSL_REQUEST_CERTIFICATE option is also set, so set it. |
593 | request_client_certificate = |
594 | request_client_certificate || require_client_certificate; |
595 | // TODO(24070, 24069): Implement setting the client certificate parameters, |
596 | // and triggering rehandshake. |
597 | } |
598 | |
599 | void SSLFilter::FreeResources() { |
600 | if (ssl_ != NULL) { |
601 | SSL_free(ssl_); |
602 | ssl_ = NULL; |
603 | } |
604 | if (socket_side_ != NULL) { |
605 | BIO_free(socket_side_); |
606 | socket_side_ = NULL; |
607 | } |
608 | if (hostname_ != NULL) { |
609 | free(hostname_); |
610 | hostname_ = NULL; |
611 | } |
612 | for (int i = 0; i < kNumBuffers; ++i) { |
613 | if (buffers_[i] != NULL) { |
614 | delete[] buffers_[i]; |
615 | buffers_[i] = NULL; |
616 | } |
617 | } |
618 | } |
619 | |
620 | SSLFilter::~SSLFilter() { |
621 | FreeResources(); |
622 | } |
623 | |
624 | void SSLFilter::Destroy() { |
625 | for (int i = 0; i < kNumBuffers; ++i) { |
626 | if (dart_buffer_objects_[i] != NULL) { |
627 | Dart_DeletePersistentHandle(dart_buffer_objects_[i]); |
628 | dart_buffer_objects_[i] = NULL; |
629 | } |
630 | } |
631 | if (string_start_ != NULL) { |
632 | Dart_DeletePersistentHandle(string_start_); |
633 | string_start_ = NULL; |
634 | } |
635 | if (string_length_ != NULL) { |
636 | Dart_DeletePersistentHandle(string_length_); |
637 | string_length_ = NULL; |
638 | } |
639 | if (handshake_complete_ != NULL) { |
640 | Dart_DeletePersistentHandle(handshake_complete_); |
641 | handshake_complete_ = NULL; |
642 | } |
643 | if (bad_certificate_callback_ != NULL) { |
644 | Dart_DeletePersistentHandle(bad_certificate_callback_); |
645 | bad_certificate_callback_ = NULL; |
646 | } |
647 | FreeResources(); |
648 | } |
649 | |
650 | /* Read decrypted data from the filter to the circular buffer */ |
651 | int SSLFilter::ProcessReadPlaintextBuffer(int start, int end) { |
652 | int length = end - start; |
653 | int bytes_processed = 0; |
654 | if (SSL_LOG_DATA) { |
655 | Syslog::Print("Entering ProcessReadPlaintextBuffer with %d bytes\n" , |
656 | length); |
657 | } |
658 | if (length > 0) { |
659 | bytes_processed = SSL_read( |
660 | ssl_, reinterpret_cast<char*>((buffers_[kReadPlaintext] + start)), |
661 | length); |
662 | if (bytes_processed < 0) { |
663 | int error = SSL_get_error(ssl_, bytes_processed); |
664 | if (SSL_LOG_DATA) { |
665 | Syslog::Print("SSL_read returned error %d\n" , error); |
666 | } |
667 | bytes_processed = 0; |
668 | } |
669 | } |
670 | if (SSL_LOG_DATA) { |
671 | Syslog::Print("Leaving ProcessReadPlaintextBuffer read %d bytes\n" , |
672 | bytes_processed); |
673 | } |
674 | return bytes_processed; |
675 | } |
676 | |
677 | int SSLFilter::ProcessWritePlaintextBuffer(int start, int end) { |
678 | int length = end - start; |
679 | if (SSL_LOG_DATA) { |
680 | Syslog::Print("Entering ProcessWritePlaintextBuffer with %d bytes\n" , |
681 | length); |
682 | } |
683 | int bytes_processed = |
684 | SSL_write(ssl_, buffers_[kWritePlaintext] + start, length); |
685 | if (bytes_processed < 0) { |
686 | if (SSL_LOG_DATA) { |
687 | Syslog::Print("SSL_write returned error %d\n" , bytes_processed); |
688 | } |
689 | return 0; |
690 | } |
691 | if (SSL_LOG_DATA) { |
692 | Syslog::Print("Leaving ProcessWritePlaintextBuffer wrote %d bytes\n" , |
693 | bytes_processed); |
694 | } |
695 | return bytes_processed; |
696 | } |
697 | |
698 | /* Read encrypted data from the circular buffer to the filter */ |
699 | int SSLFilter::ProcessReadEncryptedBuffer(int start, int end) { |
700 | int length = end - start; |
701 | if (SSL_LOG_DATA) { |
702 | Syslog::Print("Entering ProcessReadEncryptedBuffer with %d bytes\n" , |
703 | length); |
704 | } |
705 | int bytes_processed = 0; |
706 | if (length > 0) { |
707 | bytes_processed = |
708 | BIO_write(socket_side_, buffers_[kReadEncrypted] + start, length); |
709 | if (bytes_processed <= 0) { |
710 | bool retry = BIO_should_retry(socket_side_) != 0; |
711 | if (!retry) { |
712 | if (SSL_LOG_DATA) { |
713 | Syslog::Print("BIO_write failed in ReadEncryptedBuffer\n" ); |
714 | } |
715 | } |
716 | bytes_processed = 0; |
717 | } |
718 | } |
719 | if (SSL_LOG_DATA) { |
720 | Syslog::Print("Leaving ProcessReadEncryptedBuffer read %d bytes\n" , |
721 | bytes_processed); |
722 | } |
723 | return bytes_processed; |
724 | } |
725 | |
726 | int SSLFilter::ProcessWriteEncryptedBuffer(int start, int end) { |
727 | int length = end - start; |
728 | int bytes_processed = 0; |
729 | if (SSL_LOG_DATA) { |
730 | Syslog::Print("Entering ProcessWriteEncryptedBuffer with %d bytes\n" , |
731 | length); |
732 | } |
733 | if (length > 0) { |
734 | bytes_processed = |
735 | BIO_read(socket_side_, buffers_[kWriteEncrypted] + start, length); |
736 | if (bytes_processed < 0) { |
737 | if (SSL_LOG_DATA) { |
738 | Syslog::Print("WriteEncrypted BIO_read returned error %d\n" , |
739 | bytes_processed); |
740 | } |
741 | return 0; |
742 | } else { |
743 | if (SSL_LOG_DATA) { |
744 | Syslog::Print("WriteEncrypted BIO_read wrote %d bytes\n" , |
745 | bytes_processed); |
746 | } |
747 | } |
748 | } |
749 | return bytes_processed; |
750 | } |
751 | |
752 | } // namespace bin |
753 | } // namespace dart |
754 | |
755 | #endif // !defined(DART_IO_SECURE_SOCKET_DISABLED) |
756 | |