Chromium Code Reviews| Index: runtime/bin/secure_socket_filter.cc |
| diff --git a/runtime/bin/secure_socket_filter.cc b/runtime/bin/secure_socket_filter.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..4da31680c5b33467c8025da27e8a4df6c6ae501c |
| --- /dev/null |
| +++ b/runtime/bin/secure_socket_filter.cc |
| @@ -0,0 +1,849 @@ |
| +// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file |
| +// for details. All rights reserved. Use of this source code is governed by a |
| +// BSD-style license that can be found in the LICENSE file. |
| + |
| +#if !defined(DART_IO_DISABLED) && !defined(DART_IO_SECURE_SOCKET_DISABLED) |
| + |
| +#include "bin/secure_socket_filter.h" |
| + |
| +#include <openssl/bio.h> |
| +#include <openssl/ssl.h> |
| +#include <openssl/x509.h> |
| + |
| +#include "platform/globals.h" |
|
zra
2017/06/02 22:56:24
Is this needed?
bkonyi
2017/06/05 20:25:51
Nope. Removed.
|
| + |
| +#include "bin/lockers.h" |
| +#include "bin/log.h" |
| +#include "bin/secure_socket.h" |
| +#include "bin/secure_socket_utils.h" |
| +#include "platform/text_buffer.h" |
| + |
| + |
| +// Return the error from the containing function if handle is an error handle. |
| +#define RETURN_IF_ERROR(handle) \ |
| + { \ |
| + Dart_Handle __handle = handle; \ |
| + if (Dart_IsError((__handle))) { \ |
| + return __handle; \ |
| + } \ |
| + } |
| + |
| +namespace dart { |
| +namespace bin { |
| + |
| +bool SSLFilter::library_initialized_ = false; |
| +// To protect library initialization. |
| +Mutex* SSLFilter::mutex_ = new Mutex(); |
| +int SSLFilter::filter_ssl_index; |
| + |
| +const intptr_t SSLFilter::kInternalBIOSize = 10 * KB; |
| +const intptr_t SSLFilter::kApproximateSize = |
| + sizeof(SSLFilter) + (2 * SSLFilter::kInternalBIOSize); |
| + |
| +static SSLFilter* GetFilter(Dart_NativeArguments args) { |
| + SSLFilter* filter; |
| + Dart_Handle dart_this = ThrowIfError(Dart_GetNativeArgument(args, 0)); |
| + ASSERT(Dart_IsInstance(dart_this)); |
| + ThrowIfError(Dart_GetNativeInstanceField( |
| + dart_this, SSLFilter::kSSLFilterNativeFieldIndex, |
| + reinterpret_cast<intptr_t*>(&filter))); |
| + return filter; |
| +} |
| + |
| + |
| +static void DeleteFilter(void* isolate_data, |
| + Dart_WeakPersistentHandle handle, |
| + void* context_pointer) { |
| + SSLFilter* filter = reinterpret_cast<SSLFilter*>(context_pointer); |
| + filter->Release(); |
| +} |
| + |
| + |
| +static Dart_Handle SetFilter(Dart_NativeArguments args, SSLFilter* filter) { |
| + ASSERT(filter != NULL); |
| + Dart_Handle dart_this = Dart_GetNativeArgument(args, 0); |
| + RETURN_IF_ERROR(dart_this); |
| + ASSERT(Dart_IsInstance(dart_this)); |
| + Dart_Handle err = Dart_SetNativeInstanceField( |
| + dart_this, SSLFilter::kSSLFilterNativeFieldIndex, |
| + reinterpret_cast<intptr_t>(filter)); |
| + RETURN_IF_ERROR(err); |
| + Dart_NewWeakPersistentHandle(dart_this, reinterpret_cast<void*>(filter), |
| + SSLFilter::kApproximateSize, DeleteFilter); |
| + return Dart_Null(); |
| +} |
| + |
| + |
| +int CertificateCallback(int preverify_ok, X509_STORE_CTX* store_ctx) { |
| + if (preverify_ok == 1) { |
| + return 1; |
| + } |
| + Dart_Isolate isolate = Dart_CurrentIsolate(); |
| + if (isolate == NULL) { |
| + FATAL("CertificateCallback called with no current isolate\n"); |
| + } |
| + X509* certificate = X509_STORE_CTX_get_current_cert(store_ctx); |
| + int ssl_index = SSL_get_ex_data_X509_STORE_CTX_idx(); |
| + SSL* ssl = |
| + static_cast<SSL*>(X509_STORE_CTX_get_ex_data(store_ctx, ssl_index)); |
| + SSLFilter* filter = static_cast<SSLFilter*>( |
| + SSL_get_ex_data(ssl, SSLFilter::filter_ssl_index)); |
| + Dart_Handle callback = filter->bad_certificate_callback(); |
| + if (Dart_IsNull(callback)) { |
| + return 0; |
| + } |
| + |
| + // Upref since the Dart X509 object may outlive the SecurityContext. |
| + if (certificate != NULL) { |
| + X509_up_ref(certificate); |
| + } |
| + Dart_Handle args[1]; |
| + args[0] = SSLFilter::WrappedX509Certificate(certificate); |
| + if (Dart_IsError(args[0])) { |
| + filter->callback_error = args[0]; |
| + return 0; |
| + } |
| + Dart_Handle result = Dart_InvokeClosure(callback, 1, args); |
| + if (!Dart_IsError(result) && !Dart_IsBoolean(result)) { |
| + result = Dart_NewUnhandledExceptionError(DartUtils::NewDartIOException( |
| + "HandshakeException", |
| + "BadCertificateCallback returned a value that was not a boolean", |
| + Dart_Null())); |
| + } |
| + if (Dart_IsError(result)) { |
| + filter->callback_error = result; |
| + return 0; |
| + } |
| + return DartUtils::GetBooleanValue(result); |
| +} |
| + |
| + |
| +void FUNCTION_NAME(SecureSocket_Init)(Dart_NativeArguments args) { |
| + Dart_Handle dart_this = ThrowIfError(Dart_GetNativeArgument(args, 0)); |
| + SSLFilter* filter = new SSLFilter(); |
| + Dart_Handle err = SetFilter(args, filter); |
| + if (Dart_IsError(err)) { |
| + filter->Release(); |
| + Dart_PropagateError(err); |
| + } |
| + err = filter->Init(dart_this); |
| + if (Dart_IsError(err)) { |
| + // The finalizer was set up by SetFilter. It will delete `filter` if there |
| + // is an error. |
| + filter->Destroy(); |
| + Dart_PropagateError(err); |
| + } |
| +} |
| + |
| + |
| +void FUNCTION_NAME(SecureSocket_Connect)(Dart_NativeArguments args) { |
| + Dart_Handle host_name_object = ThrowIfError(Dart_GetNativeArgument(args, 1)); |
| + Dart_Handle context_object = ThrowIfError(Dart_GetNativeArgument(args, 2)); |
| + bool is_server = DartUtils::GetBooleanValue(Dart_GetNativeArgument(args, 3)); |
| + bool request_client_certificate = |
| + DartUtils::GetBooleanValue(Dart_GetNativeArgument(args, 4)); |
| + bool require_client_certificate = |
| + DartUtils::GetBooleanValue(Dart_GetNativeArgument(args, 5)); |
| + Dart_Handle protocols_handle = ThrowIfError(Dart_GetNativeArgument(args, 6)); |
| + |
| + const char* host_name = NULL; |
| + // TODO(whesse): Is truncating a Dart string containing \0 what we want? |
| + ThrowIfError(Dart_StringToCString(host_name_object, &host_name)); |
| + |
| + SSLCertContext* context = NULL; |
| + if (!Dart_IsNull(context_object)) { |
| + ThrowIfError(Dart_GetNativeInstanceField( |
| + context_object, SSLCertContext::kSecurityContextNativeFieldIndex, |
| + reinterpret_cast<intptr_t*>(&context))); |
| + } |
| + |
| + // The protocols_handle is guaranteed to be a valid Uint8List. |
| + // It will have the correct length encoding of the protocols array. |
| + ASSERT(!Dart_IsNull(protocols_handle)); |
| + GetFilter(args)->Connect(host_name, context, is_server, |
| + request_client_certificate, |
| + require_client_certificate, protocols_handle); |
| +} |
| + |
| + |
| +void FUNCTION_NAME(SecureSocket_Destroy)(Dart_NativeArguments args) { |
| + SSLFilter* filter = GetFilter(args); |
| + // There are two paths that can clean up an SSLFilter object. First, |
| + // there is this explicit call to Destroy(), called from |
| + // _SecureFilter.destroy() in Dart code. After a call to destroy(), the Dart |
| + // code maintains the invariant that there will be no futher SSLFilter |
| + // requests sent to the IO Service. Therefore, the internals of the SSLFilter |
| + // are safe to deallocate, but not the SSLFilter itself, which is already |
| + // set up to be cleaned up by the finalizer. |
| + // |
| + // The second path is through the finalizer, which we have to do in case |
| + // some mishap prevents a call to _SecureFilter.destroy(). |
| + filter->Destroy(); |
| +} |
| + |
| + |
| +void FUNCTION_NAME(SecureSocket_Handshake)(Dart_NativeArguments args) { |
| + GetFilter(args)->Handshake(); |
| +} |
| + |
| + |
| +void FUNCTION_NAME(SecureSocket_GetSelectedProtocol)( |
| + Dart_NativeArguments args) { |
| + GetFilter(args)->GetSelectedProtocol(args); |
| +} |
| + |
| + |
| +void FUNCTION_NAME(SecureSocket_Renegotiate)(Dart_NativeArguments args) { |
| + bool use_session_cache = |
| + DartUtils::GetBooleanValue(Dart_GetNativeArgument(args, 1)); |
| + bool request_client_certificate = |
| + DartUtils::GetBooleanValue(Dart_GetNativeArgument(args, 2)); |
| + bool require_client_certificate = |
| + DartUtils::GetBooleanValue(Dart_GetNativeArgument(args, 3)); |
| + GetFilter(args)->Renegotiate(use_session_cache, request_client_certificate, |
| + require_client_certificate); |
| +} |
| + |
| + |
| +void FUNCTION_NAME(SecureSocket_RegisterHandshakeCompleteCallback)( |
| + Dart_NativeArguments args) { |
| + Dart_Handle handshake_complete = |
| + ThrowIfError(Dart_GetNativeArgument(args, 1)); |
| + if (!Dart_IsClosure(handshake_complete)) { |
| + Dart_ThrowException(DartUtils::NewDartArgumentError( |
| + "Illegal argument to RegisterHandshakeCompleteCallback")); |
| + } |
| + GetFilter(args)->RegisterHandshakeCompleteCallback(handshake_complete); |
| +} |
| + |
| + |
| +void FUNCTION_NAME(SecureSocket_RegisterBadCertificateCallback)( |
| + Dart_NativeArguments args) { |
| + Dart_Handle callback = ThrowIfError(Dart_GetNativeArgument(args, 1)); |
| + if (!Dart_IsClosure(callback) && !Dart_IsNull(callback)) { |
| + Dart_ThrowException(DartUtils::NewDartArgumentError( |
| + "Illegal argument to RegisterBadCertificateCallback")); |
| + } |
| + GetFilter(args)->RegisterBadCertificateCallback(callback); |
| +} |
| + |
| + |
| +void FUNCTION_NAME(SecureSocket_PeerCertificate)(Dart_NativeArguments args) { |
| + Dart_Handle cert = ThrowIfError(GetFilter(args)->PeerCertificate()); |
| + Dart_SetReturnValue(args, cert); |
| +} |
| + |
| + |
| +void FUNCTION_NAME(SecureSocket_FilterPointer)(Dart_NativeArguments args) { |
| + SSLFilter* filter = GetFilter(args); |
| + // This filter pointer is passed to the IO Service thread. The IO Service |
| + // thread must Release() the pointer when it is done with it. |
| + filter->Retain(); |
| + intptr_t filter_pointer = reinterpret_cast<intptr_t>(filter); |
| + Dart_SetReturnValue(args, Dart_NewInteger(filter_pointer)); |
| +} |
| + |
| + |
| +/** |
| + * Pushes data through the SSL filter, reading and writing from circular |
| + * buffers shared with Dart. |
| + * |
| + * The Dart _SecureFilterImpl class contains 4 ExternalByteArrays used to |
| + * pass encrypted and plaintext data to and from the C++ SSLFilter object. |
| + * |
| + * ProcessFilter is called with a CObject array containing the pointer to |
| + * the SSLFilter, encoded as an int, and the start and end positions of the |
| + * valid data in the four circular buffers. The function only reads from |
| + * the valid data area of the input buffers, and only writes to the free |
| + * area of the output buffers. The function returns the new start and end |
| + * positions in the buffers, but it only updates start for input buffers, and |
| + * end for output buffers. Therefore, the Dart thread can simultaneously |
| + * write to the free space and end pointer of input buffers, and read from |
| + * the data space of output buffers, and modify the start pointer. |
| + * |
| + * When ProcessFilter returns, the Dart thread is responsible for combining |
| + * the updated pointers from Dart and C++, to make the new valid state of |
| + * the circular buffer. |
| + */ |
| +CObject* SSLFilter::ProcessFilterRequest(const CObjectArray& request) { |
| + CObjectIntptr filter_object(request[0]); |
| + SSLFilter* filter = reinterpret_cast<SSLFilter*>(filter_object.Value()); |
| + RefCntReleaseScope<SSLFilter> rs(filter); |
| + |
| + bool in_handshake = CObjectBool(request[1]).Value(); |
| + int starts[SSLFilter::kNumBuffers]; |
| + int ends[SSLFilter::kNumBuffers]; |
| + for (int i = 0; i < SSLFilter::kNumBuffers; ++i) { |
| + starts[i] = CObjectInt32(request[2 * i + 2]).Value(); |
| + ends[i] = CObjectInt32(request[2 * i + 3]).Value(); |
| + } |
| + |
| + if (filter->ProcessAllBuffers(starts, ends, in_handshake)) { |
| + CObjectArray* result = |
| + new CObjectArray(CObject::NewArray(SSLFilter::kNumBuffers * 2)); |
| + for (int i = 0; i < SSLFilter::kNumBuffers; ++i) { |
| + result->SetAt(2 * i, new CObjectInt32(CObject::NewInt32(starts[i]))); |
| + result->SetAt(2 * i + 1, new CObjectInt32(CObject::NewInt32(ends[i]))); |
| + } |
| + return result; |
| + } else { |
| + int32_t error_code = static_cast<int32_t>(ERR_peek_error()); |
| + TextBuffer error_string(SecureSocketUtils::SSL_ERROR_MESSAGE_BUFFER_SIZE); |
| + SecureSocketUtils::FetchErrorString(filter->ssl_, &error_string); |
| + CObjectArray* result = new CObjectArray(CObject::NewArray(2)); |
| + result->SetAt(0, new CObjectInt32(CObject::NewInt32(error_code))); |
| + result->SetAt(1, new CObjectString(CObject::NewString(error_string.buf()))); |
| + return result; |
| + } |
| +} |
| + |
| + |
| +bool SSLFilter::ProcessAllBuffers(int starts[kNumBuffers], |
| + int ends[kNumBuffers], |
| + bool in_handshake) { |
| + for (int i = 0; i < kNumBuffers; ++i) { |
| + if (in_handshake && (i == kReadPlaintext || i == kWritePlaintext)) continue; |
| + int start = starts[i]; |
| + int end = ends[i]; |
| + int size = IsBufferEncrypted(i) ? encrypted_buffer_size_ : buffer_size_; |
| + if (start < 0 || end < 0 || start >= size || end >= size) { |
| + FATAL("Out-of-bounds internal buffer access in dart:io SecureSocket"); |
| + } |
| + switch (i) { |
| + case kReadPlaintext: |
| + case kWriteEncrypted: |
| + // Write data to the circular buffer's free space. If the buffer |
| + // is full, neither if statement is executed and nothing happens. |
| + if (start <= end) { |
| + // If the free space may be split into two segments, |
| + // then the first is [end, size), unless start == 0. |
| + // Then, since the last free byte is at position start - 2, |
| + // the interval is [end, size - 1). |
| + int buffer_end = (start == 0) ? size - 1 : size; |
| + int bytes = (i == kReadPlaintext) |
| + ? ProcessReadPlaintextBuffer(end, buffer_end) |
| + : ProcessWriteEncryptedBuffer(end, buffer_end); |
| + if (bytes < 0) return false; |
| + end += bytes; |
| + ASSERT(end <= size); |
| + if (end == size) end = 0; |
| + } |
| + if (start > end + 1) { |
| + int bytes = (i == kReadPlaintext) |
| + ? ProcessReadPlaintextBuffer(end, start - 1) |
| + : ProcessWriteEncryptedBuffer(end, start - 1); |
| + if (bytes < 0) return false; |
| + end += bytes; |
| + ASSERT(end < start); |
| + } |
| + ends[i] = end; |
| + break; |
| + case kReadEncrypted: |
| + case kWritePlaintext: |
| + // Read/Write data from circular buffer. If the buffer is empty, |
| + // neither if statement's condition is true. |
| + if (end < start) { |
| + // Data may be split into two segments. In this case, |
| + // the first is [start, size). |
| + int bytes = (i == kReadEncrypted) |
| + ? ProcessReadEncryptedBuffer(start, size) |
| + : ProcessWritePlaintextBuffer(start, size); |
| + if (bytes < 0) return false; |
| + start += bytes; |
| + ASSERT(start <= size); |
| + if (start == size) start = 0; |
| + } |
| + if (start < end) { |
| + int bytes = (i == kReadEncrypted) |
| + ? ProcessReadEncryptedBuffer(start, end) |
| + : ProcessWritePlaintextBuffer(start, end); |
| + if (bytes < 0) return false; |
| + start += bytes; |
| + ASSERT(start <= end); |
| + } |
| + starts[i] = start; |
| + break; |
| + default: |
| + UNREACHABLE(); |
| + } |
| + } |
| + return true; |
| +} |
| + |
| + |
| +Dart_Handle SSLFilter::Init(Dart_Handle dart_this) { |
| + if (!library_initialized_) { |
| + InitializeLibrary(); |
| + } |
| + ASSERT(string_start_ == NULL); |
| + string_start_ = Dart_NewPersistentHandle(DartUtils::NewString("start")); |
| + ASSERT(string_start_ != NULL); |
| + ASSERT(string_length_ == NULL); |
| + string_length_ = Dart_NewPersistentHandle(DartUtils::NewString("length")); |
| + ASSERT(string_length_ != NULL); |
| + ASSERT(bad_certificate_callback_ == NULL); |
| + bad_certificate_callback_ = Dart_NewPersistentHandle(Dart_Null()); |
| + ASSERT(bad_certificate_callback_ != NULL); |
| + // Caller handles cleanup on an error. |
| + return InitializeBuffers(dart_this); |
| +} |
| + |
| + |
| +Dart_Handle SSLFilter::InitializeBuffers(Dart_Handle dart_this) { |
| + // Create SSLFilter buffers as ExternalUint8Array objects. |
| + Dart_Handle buffers_string = DartUtils::NewString("buffers"); |
| + RETURN_IF_ERROR(buffers_string); |
| + Dart_Handle dart_buffers_object = Dart_GetField(dart_this, buffers_string); |
| + RETURN_IF_ERROR(dart_buffers_object); |
| + Dart_Handle secure_filter_impl_type = Dart_InstanceGetType(dart_this); |
| + RETURN_IF_ERROR(secure_filter_impl_type); |
| + Dart_Handle size_string = DartUtils::NewString("SIZE"); |
| + RETURN_IF_ERROR(size_string); |
| + Dart_Handle dart_buffer_size = |
| + Dart_GetField(secure_filter_impl_type, size_string); |
| + RETURN_IF_ERROR(dart_buffer_size); |
| + |
| + int64_t buffer_size = 0; |
| + Dart_Handle err = Dart_IntegerToInt64(dart_buffer_size, &buffer_size); |
| + RETURN_IF_ERROR(err); |
| + |
| + Dart_Handle encrypted_size_string = DartUtils::NewString("ENCRYPTED_SIZE"); |
| + RETURN_IF_ERROR(encrypted_size_string); |
| + |
| + Dart_Handle dart_encrypted_buffer_size = |
| + Dart_GetField(secure_filter_impl_type, encrypted_size_string); |
| + RETURN_IF_ERROR(dart_encrypted_buffer_size); |
| + |
| + int64_t encrypted_buffer_size = 0; |
| + err = Dart_IntegerToInt64(dart_encrypted_buffer_size, &encrypted_buffer_size); |
| + RETURN_IF_ERROR(err); |
| + |
| + if (buffer_size <= 0 || buffer_size > 1 * MB) { |
| + FATAL("Invalid buffer size in _ExternalBuffer"); |
| + } |
| + if (encrypted_buffer_size <= 0 || encrypted_buffer_size > 1 * MB) { |
| + FATAL("Invalid encrypted buffer size in _ExternalBuffer"); |
| + } |
| + buffer_size_ = static_cast<int>(buffer_size); |
| + encrypted_buffer_size_ = static_cast<int>(encrypted_buffer_size); |
| + |
| + Dart_Handle data_identifier = DartUtils::NewString("data"); |
| + RETURN_IF_ERROR(data_identifier); |
| + |
| + for (int i = 0; i < kNumBuffers; i++) { |
| + int size = IsBufferEncrypted(i) ? encrypted_buffer_size_ : buffer_size_; |
| + buffers_[i] = new uint8_t[size]; |
| + ASSERT(buffers_[i] != NULL); |
| + dart_buffer_objects_[i] = NULL; |
| + } |
| + |
| + Dart_Handle result = Dart_Null(); |
| + for (int i = 0; i < kNumBuffers; ++i) { |
| + int size = IsBufferEncrypted(i) ? encrypted_buffer_size_ : buffer_size_; |
| + result = Dart_ListGetAt(dart_buffers_object, i); |
| + if (Dart_IsError(result)) { |
| + break; |
| + } |
| + |
| + dart_buffer_objects_[i] = Dart_NewPersistentHandle(result); |
| + ASSERT(dart_buffer_objects_[i] != NULL); |
| + Dart_Handle data = |
| + Dart_NewExternalTypedData(Dart_TypedData_kUint8, buffers_[i], size); |
| + if (Dart_IsError(data)) { |
| + result = data; |
| + break; |
| + } |
| + result = Dart_HandleFromPersistent(dart_buffer_objects_[i]); |
| + if (Dart_IsError(result)) { |
| + break; |
| + } |
| + result = Dart_SetField(result, data_identifier, data); |
| + if (Dart_IsError(result)) { |
| + break; |
| + } |
| + } |
| + |
| + // Caller handles cleanup on an error. |
| + return result; |
| +} |
| + |
| + |
| +void SSLFilter::RegisterHandshakeCompleteCallback(Dart_Handle complete) { |
| + ASSERT(NULL == handshake_complete_); |
| + handshake_complete_ = Dart_NewPersistentHandle(complete); |
| + |
| + ASSERT(handshake_complete_ != NULL); |
| +} |
| + |
| + |
| +void SSLFilter::RegisterBadCertificateCallback(Dart_Handle callback) { |
| + ASSERT(bad_certificate_callback_ != NULL); |
| + Dart_DeletePersistentHandle(bad_certificate_callback_); |
| + bad_certificate_callback_ = Dart_NewPersistentHandle(callback); |
| + ASSERT(bad_certificate_callback_ != NULL); |
| +} |
| + |
| + |
| +Dart_Handle SSLFilter::PeerCertificate() { |
| + X509* ca = SSL_get_peer_certificate(ssl_); |
| + if (ca == NULL) { |
| + return Dart_Null(); |
| + } |
| + return SSLFilter::WrappedX509Certificate(ca); |
| +} |
| + |
| + |
| +void SSLFilter::InitializeLibrary() { |
| + MutexLocker locker(mutex_); |
| + if (!library_initialized_) { |
| + SSL_library_init(); |
| + filter_ssl_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL); |
| + ASSERT(filter_ssl_index >= 0); |
| + library_initialized_ = true; |
| + } |
| +} |
| + |
| + |
| +void SSLFilter::Connect(const char* hostname, |
| + SSLCertContext* context, |
| + bool is_server, |
| + bool request_client_certificate, |
| + bool require_client_certificate, |
| + Dart_Handle protocols_handle) { |
| + is_server_ = is_server; |
| + if (in_handshake_) { |
| + FATAL("Connect called twice on the same _SecureFilter."); |
| + } |
| + |
| + int status; |
| + int error; |
| + BIO* ssl_side; |
| + status = BIO_new_bio_pair(&ssl_side, kInternalBIOSize, &socket_side_, |
| + kInternalBIOSize); |
| + SecureSocketUtils::CheckStatusSSL(status, "TlsException", "BIO_new_bio_pair", |
| + ssl_); |
| + |
| + ASSERT(context != NULL); |
| + ASSERT(context->context() != NULL); |
| + ssl_ = SSL_new(context->context()); |
| + SSL_set_bio(ssl_, ssl_side, ssl_side); |
| + SSL_set_mode(ssl_, SSL_MODE_AUTO_RETRY); // TODO(whesse): Is this right? |
| + SSL_set_ex_data(ssl_, filter_ssl_index, this); |
| + RegisterCallbacks(context); |
| + |
| + if (is_server_) { |
| + int certificate_mode = |
| + request_client_certificate ? SSL_VERIFY_PEER : SSL_VERIFY_NONE; |
| + if (require_client_certificate) { |
| + certificate_mode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT; |
| + } |
| + SSL_set_verify(ssl_, certificate_mode, NULL); |
| + } else { |
| + SSLCertContext::SetAlpnProtocolList(protocols_handle, ssl_, NULL, false); |
| + status = SSL_set_tlsext_host_name(ssl_, hostname); |
| + SecureSocketUtils::CheckStatusSSL(status, "TlsException", |
| + "Set SNI host name", ssl_); |
| + // Sets the hostname in the certificate-checking object, so it is checked |
| + // against the certificate presented by the server. |
| + X509_VERIFY_PARAM* certificate_checking_parameters = SSL_get0_param(ssl_); |
| + hostname_ = strdup(hostname); |
| + X509_VERIFY_PARAM_set_flags( |
| + certificate_checking_parameters, |
| + X509_V_FLAG_PARTIAL_CHAIN | X509_V_FLAG_TRUSTED_FIRST); |
| + X509_VERIFY_PARAM_set_hostflags(certificate_checking_parameters, 0); |
| + status = X509_VERIFY_PARAM_set1_host(certificate_checking_parameters, |
| + hostname_, strlen(hostname_)); |
| + SecureSocketUtils::CheckStatusSSL( |
| + status, "TlsException", "Set hostname for certificate checking", ssl_); |
| + } |
| + // Make the connection: |
| + if (is_server_) { |
| + status = SSL_accept(ssl_); |
| + if (SSL_LOG_STATUS) { |
| + Log::Print("SSL_accept status: %d\n", status); |
| + } |
| + if (status != 1) { |
| + // TODO(whesse): expect a needs-data error here. Handle other errors. |
| + error = SSL_get_error(ssl_, status); |
| + if (SSL_LOG_STATUS) { |
| + Log::Print("SSL_accept error: %d\n", error); |
| + } |
| + } |
| + } else { |
| + status = SSL_connect(ssl_); |
| + if (SSL_LOG_STATUS) { |
| + Log::Print("SSL_connect status: %d\n", status); |
| + } |
| + if (status != 1) { |
| + // TODO(whesse): expect a needs-data error here. Handle other errors. |
| + error = SSL_get_error(ssl_, status); |
| + if (SSL_LOG_STATUS) { |
| + Log::Print("SSL_connect error: %d\n", error); |
| + } |
| + } |
| + } |
| + Handshake(); |
| +} |
| + |
| + |
| +int printErrorCallback(const char* str, size_t len, void* ctx) { |
|
zra
2017/06/02 22:56:24
Is this used?
bkonyi
2017/06/05 20:25:51
Nope. Removed.
|
| + Log::PrintErr("%.*s\n", static_cast<int>(len), str); |
| + return 1; |
| +} |
| + |
| + |
| +void SSLFilter::Handshake() { |
| + // Try and push handshake along. |
| + int status; |
| + status = SSL_do_handshake(ssl_); |
| + if (callback_error != NULL) { |
| + // The SSL_do_handshake will try performing a handshake and might call |
| + // a CertificateCallback. If the certificate validation |
| + // failed the 'callback_error" will be set by the certificateCallback |
| + // logic and we propagate the error" |
| + Dart_PropagateError(callback_error); |
| + } |
| + if (SSL_want_write(ssl_) || SSL_want_read(ssl_)) { |
| + in_handshake_ = true; |
| + return; |
| + } |
| + SecureSocketUtils::CheckStatusSSL( |
| + status, "HandshakeException", |
| + is_server_ ? "Handshake error in server" : "Handshake error in client", |
| + ssl_); |
| + // Handshake succeeded. |
| + if (in_handshake_) { |
| + // TODO(24071): Check return value of SSL_get_verify_result, this |
| + // should give us the hostname check. |
| + int result = SSL_get_verify_result(ssl_); |
| + if (SSL_LOG_STATUS) { |
| + Log::Print("Handshake verification status: %d\n", result); |
| + X509* peer_certificate = SSL_get_peer_certificate(ssl_); |
| + if (peer_certificate == NULL) { |
| + Log::Print("No peer certificate received\n"); |
| + } else { |
| + X509_NAME* s_name = X509_get_subject_name(peer_certificate); |
| + printf("Peer certificate SN: "); |
| + X509_NAME_print_ex_fp(stdout, s_name, 4, 0); |
| + printf("\n"); |
| + } |
| + } |
| + ThrowIfError(Dart_InvokeClosure( |
| + Dart_HandleFromPersistent(handshake_complete_), 0, NULL)); |
| + in_handshake_ = false; |
| + } |
| +} |
| + |
| + |
| +void SSLFilter::GetSelectedProtocol(Dart_NativeArguments args) { |
| + const uint8_t* protocol; |
| + unsigned length; |
| + SSL_get0_alpn_selected(ssl_, &protocol, &length); |
| + if (length == 0) { |
| + Dart_SetReturnValue(args, Dart_Null()); |
| + } else { |
| + Dart_SetReturnValue(args, Dart_NewStringFromUTF8(protocol, length)); |
| + } |
| +} |
| + |
| + |
| +void SSLFilter::Renegotiate(bool use_session_cache, |
| + bool request_client_certificate, |
| + bool require_client_certificate) { |
| + // The SSL_REQUIRE_CERTIFICATE option only takes effect if the |
| + // SSL_REQUEST_CERTIFICATE option is also set, so set it. |
| + request_client_certificate = |
| + request_client_certificate || require_client_certificate; |
| + // TODO(24070, 24069): Implement setting the client certificate parameters, |
| + // and triggering rehandshake. |
| +} |
| + |
| + |
| +static void ReleaseCertificate(void* isolate_data, |
| + Dart_WeakPersistentHandle handle, |
| + void* context_pointer) { |
| + X509* cert = reinterpret_cast<X509*>(context_pointer); |
| + X509_free(cert); |
| +} |
| + |
| + |
| +static intptr_t EstimateX509Size(X509* certificate) { |
| + intptr_t length = i2d_X509(certificate, NULL); |
| + return length > 0 ? length : 0; |
| +} |
| + |
| + |
| +// Returns the handle for a Dart object wrapping the X509 certificate object. |
| +// The caller should own a reference to the X509 object whose reference count |
| +// won't drop to zero before the ReleaseCertificate finalizer runs. |
| +Dart_Handle SSLFilter::WrappedX509Certificate(X509* certificate) { |
| + if (certificate == NULL) { |
| + return Dart_Null(); |
| + } |
| + Dart_Handle x509_type = |
| + DartUtils::GetDartType(DartUtils::kIOLibURL, "X509Certificate"); |
| + if (Dart_IsError(x509_type)) { |
| + X509_free(certificate); |
| + return x509_type; |
| + } |
| + Dart_Handle arguments[] = {NULL}; |
| + Dart_Handle result = |
| + Dart_New(x509_type, DartUtils::NewString("_"), 0, arguments); |
| + if (Dart_IsError(result)) { |
| + X509_free(certificate); |
| + return result; |
| + } |
| + ASSERT(Dart_IsInstance(result)); |
| + Dart_Handle status = |
| + Dart_SetNativeInstanceField(result, SSLCertContext::kX509NativeFieldIndex, |
| + reinterpret_cast<intptr_t>(certificate)); |
| + if (Dart_IsError(status)) { |
| + X509_free(certificate); |
| + return status; |
| + } |
| + const intptr_t approximate_size_of_certificate = |
| + sizeof(*certificate) + EstimateX509Size(certificate); |
| + ASSERT(approximate_size_of_certificate > 0); |
| + Dart_NewWeakPersistentHandle(result, reinterpret_cast<void*>(certificate), |
| + approximate_size_of_certificate, |
| + ReleaseCertificate); |
| + return result; |
| +} |
| + |
| + |
| +void SSLFilter::FreeResources() { |
| + if (ssl_ != NULL) { |
| + SSL_free(ssl_); |
| + ssl_ = NULL; |
| + } |
| + if (socket_side_ != NULL) { |
| + BIO_free(socket_side_); |
| + socket_side_ = NULL; |
| + } |
| + if (hostname_ != NULL) { |
| + free(hostname_); |
| + hostname_ = NULL; |
| + } |
| + for (int i = 0; i < kNumBuffers; ++i) { |
| + if (buffers_[i] != NULL) { |
| + delete[] buffers_[i]; |
| + buffers_[i] = NULL; |
| + } |
| + } |
| +} |
| + |
| + |
| +SSLFilter::~SSLFilter() { |
| + FreeResources(); |
| +} |
| + |
| + |
| +void SSLFilter::Destroy() { |
| + for (int i = 0; i < kNumBuffers; ++i) { |
| + if (dart_buffer_objects_[i] != NULL) { |
| + Dart_DeletePersistentHandle(dart_buffer_objects_[i]); |
| + dart_buffer_objects_[i] = NULL; |
| + } |
| + } |
| + if (string_start_ != NULL) { |
| + Dart_DeletePersistentHandle(string_start_); |
| + string_start_ = NULL; |
| + } |
| + if (string_length_ != NULL) { |
| + Dart_DeletePersistentHandle(string_length_); |
| + string_length_ = NULL; |
| + } |
| + if (handshake_complete_ != NULL) { |
| + Dart_DeletePersistentHandle(handshake_complete_); |
| + handshake_complete_ = NULL; |
| + } |
| + if (bad_certificate_callback_ != NULL) { |
| + Dart_DeletePersistentHandle(bad_certificate_callback_); |
| + bad_certificate_callback_ = NULL; |
| + } |
| + FreeResources(); |
| +} |
| + |
| + |
| +/* Read decrypted data from the filter to the circular buffer */ |
| +int SSLFilter::ProcessReadPlaintextBuffer(int start, int end) { |
| + int length = end - start; |
| + int bytes_processed = 0; |
| + if (length > 0) { |
| + bytes_processed = SSL_read( |
| + ssl_, reinterpret_cast<char*>((buffers_[kReadPlaintext] + start)), |
| + length); |
| + if (bytes_processed < 0) { |
| + int error = SSL_get_error(ssl_, bytes_processed); |
| + USE(error); |
| + bytes_processed = 0; |
| + } |
| + } |
| + return bytes_processed; |
| +} |
| + |
| + |
| +int SSLFilter::ProcessWritePlaintextBuffer(int start, int end) { |
| + int length = end - start; |
| + int bytes_processed = |
| + SSL_write(ssl_, buffers_[kWritePlaintext] + start, length); |
| + if (bytes_processed < 0) { |
| + if (SSL_LOG_DATA) { |
| + Log::Print("SSL_write returned error %d\n", bytes_processed); |
| + } |
| + return 0; |
| + } |
| + return bytes_processed; |
| +} |
| + |
| + |
| +/* Read encrypted data from the circular buffer to the filter */ |
| +int SSLFilter::ProcessReadEncryptedBuffer(int start, int end) { |
| + int length = end - start; |
| + if (SSL_LOG_DATA) |
| + Log::Print("Entering ProcessReadEncryptedBuffer with %d bytes\n", length); |
| + int bytes_processed = 0; |
| + if (length > 0) { |
| + bytes_processed = |
| + BIO_write(socket_side_, buffers_[kReadEncrypted] + start, length); |
| + if (bytes_processed <= 0) { |
| + bool retry = BIO_should_retry(socket_side_); |
| + if (!retry) { |
| + if (SSL_LOG_DATA) |
| + Log::Print("BIO_write failed in ReadEncryptedBuffer\n"); |
| + } |
| + bytes_processed = 0; |
| + } |
| + } |
| + if (SSL_LOG_DATA) |
| + Log::Print("Leaving ProcessReadEncryptedBuffer wrote %d bytes\n", |
| + bytes_processed); |
| + return bytes_processed; |
| +} |
| + |
| + |
| +int SSLFilter::ProcessWriteEncryptedBuffer(int start, int end) { |
| + int length = end - start; |
| + int bytes_processed = 0; |
| + if (length > 0) { |
| + bytes_processed = |
| + BIO_read(socket_side_, buffers_[kWriteEncrypted] + start, length); |
| + if (bytes_processed < 0) { |
| + if (SSL_LOG_DATA) |
| + Log::Print("WriteEncrypted BIO_read returned error %d\n", |
| + bytes_processed); |
| + return 0; |
| + } else { |
| + if (SSL_LOG_DATA) |
| + Log::Print("WriteEncrypted BIO_read wrote %d bytes\n", |
| + bytes_processed); |
| + } |
| + } |
| + return bytes_processed; |
| +} |
| + |
| +} // namespace bin |
| +} // namespace dart |
| + |
| +#endif // !defined(DART_IO_DISABLED) && |
| + // !defined(DART_IO_SECURE_SOCKET_DISABLED) |