Version 1.24.3
Cherry-pick 0af5298845574517862e0a4cfabace1488ec2196 to stable
Cherry-pick 644862bf966e6c079460258807ab3364b7e3759a to stable
Cherry-pick 0e164c043f1007e8b3124a152ac9783f821e038c to stable
Cherry-pick 9cf7ba4f842a126b581eeac0197c269a204ecd78 to stable
diff --git a/CHANGELOG.md b/CHANGELOG.md
index befe4a0..cf29c44 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,17 @@
+## 1.24.3 - 14-12-2017
+
+* Fix for constructing a new SecurityContext that contains the built-in
+ certificate authority roots
+ (https://github.com/dart-lang/sdk/issues/24693).
+
+### Core library changes
+
+* `dart:io`
+ * Unified backends for `SecureSocket`, `SecurityContext`, and
+ `X509Certificate` to be consistent across all platforms. All
+ `SecureSocket`, `SecurityContext`, and `X509Certificate` properties and
+ methods are now supported on iOS and OSX.
+
## 1.24.2 - 22-06-2017
* Fixes for debugging in Dartium.
diff --git a/pkg/dev_compiler/tool/input_sdk/patch/io_patch.dart b/pkg/dev_compiler/tool/input_sdk/patch/io_patch.dart
index 59aa7ff..688cf4a 100644
--- a/pkg/dev_compiler/tool/input_sdk/patch/io_patch.dart
+++ b/pkg/dev_compiler/tool/input_sdk/patch/io_patch.dart
@@ -451,7 +451,7 @@
@patch
class SecurityContext {
@patch
- factory SecurityContext() {
+ factory SecurityContext({bool withTrustedRoots: false}) {
throw new UnsupportedError("SecurityContext constructor");
}
diff --git a/runtime/bin/BUILD.gn b/runtime/bin/BUILD.gn
index cde7664..f0ac8d9 100644
--- a/runtime/bin/BUILD.gn
+++ b/runtime/bin/BUILD.gn
@@ -408,10 +408,10 @@
if (is_mac) {
libs += [ "CoreServices.framework" ]
}
- } else {
- deps += [ "//third_party/boringssl" ]
}
+ deps += [ "//third_party/boringssl" ]
+
if (is_fuchsia) {
libs = [ "launchpad" ]
}
diff --git a/runtime/bin/io_impl_sources.gypi b/runtime/bin/io_impl_sources.gypi
index 5a7df0e..d3bce7e 100644
--- a/runtime/bin/io_impl_sources.gypi
+++ b/runtime/bin/io_impl_sources.gypi
@@ -53,14 +53,18 @@
'process_win.cc',
'reference_counting.h',
'root_certificates_unsupported.cc',
- 'secure_socket.h',
- 'secure_socket_boringssl.cc',
- 'secure_socket_boringssl.h',
- 'secure_socket_ios.cc',
- 'secure_socket_ios.h',
- 'secure_socket_macos.cc',
- 'secure_socket_macos.h',
+ 'secure_socket_filter.cc',
+ 'secure_socket_filter.h',
'secure_socket_unsupported.cc',
+ 'secure_socket_utils.cc',
+ 'secure_socket_utils.h',
+ 'security_context.cc',
+ 'security_context.h',
+ 'security_context_android.cc',
+ 'security_context_fuchsia.cc',
+ 'security_context_linux.cc',
+ 'security_context_macos.cc',
+ 'security_context_win.cc',
'socket.cc',
'socket.h',
'socket_android.cc',
diff --git a/runtime/bin/io_service.cc b/runtime/bin/io_service.cc
index 2a685f1..c5e5628 100644
--- a/runtime/bin/io_service.cc
+++ b/runtime/bin/io_service.cc
@@ -10,7 +10,8 @@
#include "bin/directory.h"
#include "bin/file.h"
#include "bin/io_buffer.h"
-#include "bin/secure_socket.h"
+#include "bin/secure_socket_filter.h"
+#include "bin/security_context.h"
#include "bin/socket.h"
#include "bin/utils.h"
diff --git a/runtime/bin/main.cc b/runtime/bin/main.cc
index 412a2fb2..647490e 100644
--- a/runtime/bin/main.cc
+++ b/runtime/bin/main.cc
@@ -518,7 +518,6 @@
}
-#if !defined(HOST_OS_MACOS)
extern const char* commandline_root_certs_file;
extern const char* commandline_root_certs_cache;
@@ -554,7 +553,6 @@
commandline_root_certs_cache = arg;
return true;
}
-#endif // !defined(HOST_OS_MACOS)
static struct {
@@ -590,10 +588,8 @@
{"--hot-reload-rollback-test-mode", ProcessHotReloadRollbackTestModeOption},
{"--short_socket_read", ProcessShortSocketReadOption},
{"--short_socket_write", ProcessShortSocketWriteOption},
-#if !defined(HOST_OS_MACOS)
{"--root-certs-file=", ProcessRootCertsFileOption},
{"--root-certs-cache=", ProcessRootCertsCacheOption},
-#endif // !defined(HOST_OS_MACOS)
{NULL, NULL}};
diff --git a/runtime/bin/secure_socket.h b/runtime/bin/secure_socket.h
deleted file mode 100644
index 83bf34d..0000000
--- a/runtime/bin/secure_socket.h
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright (c) 2012, 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.
-
-#ifndef RUNTIME_BIN_SECURE_SOCKET_H_
-#define RUNTIME_BIN_SECURE_SOCKET_H_
-
-#if defined(DART_IO_DISABLED) || defined(DART_IO_SECURE_SOCKET_DISABLED)
-#error "secure_socket.h can only be included on builds with SSL enabled"
-#endif
-
-#include "platform/globals.h"
-#if defined(HOST_OS_ANDROID) || defined(HOST_OS_LINUX) || \
- defined(HOST_OS_WINDOWS) || defined(HOST_OS_FUCHSIA)
-#include "bin/secure_socket_boringssl.h"
-#elif defined(HOST_OS_MACOS)
-#if HOST_OS_IOS
-#include "bin/secure_socket_ios.h"
-#else // HOST_OS_IOS
-#include "bin/secure_socket_macos.h"
-#endif // HOST_OS_IOS
-#else
-#error Unknown target os.
-#endif
-
-#endif // RUNTIME_BIN_SECURE_SOCKET_H_
diff --git a/runtime/bin/secure_socket_boringssl.cc b/runtime/bin/secure_socket_boringssl.cc
deleted file mode 100644
index a3740a8..0000000
--- a/runtime/bin/secure_socket_boringssl.cc
+++ /dev/null
@@ -1,1807 +0,0 @@
-// Copyright (c) 2012, 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 "platform/globals.h"
-#if defined(HOST_OS_ANDROID) || defined(HOST_OS_LINUX) || \
- defined(HOST_OS_WINDOWS) || defined(HOST_OS_FUCHSIA)
-
-#include "bin/secure_socket.h"
-#include "bin/secure_socket_boringssl.h"
-
-#include <errno.h>
-#include <fcntl.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/stat.h>
-
-#include <openssl/bio.h>
-#include <openssl/err.h>
-#include <openssl/pkcs12.h>
-#include <openssl/safestack.h>
-#include <openssl/ssl.h>
-#include <openssl/tls1.h>
-#include <openssl/x509.h>
-
-#include "bin/builtin.h"
-#include "bin/dartutils.h"
-#include "bin/directory.h"
-#include "bin/file.h"
-#include "bin/lockers.h"
-#include "bin/log.h"
-#include "bin/socket.h"
-#include "bin/thread.h"
-#include "bin/utils.h"
-#include "platform/text_buffer.h"
-#include "platform/utils.h"
-
-#include "include/dart_api.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);
-
-// The security context won't necessarily use the compiled-in root certificates,
-// but since there is no way to update the size of the allocation after creating
-// the weak persistent handle, we assume that it will. Note that when the
-// root certs aren't compiled in, |root_certificates_pem_length| is 0.
-const intptr_t SSLContext::kApproximateSize =
- sizeof(SSLContext) + root_certificates_pem_length;
-
-static const int kSSLFilterNativeFieldIndex = 0;
-static const int kSecurityContextNativeFieldIndex = 0;
-static const int kX509NativeFieldIndex = 0;
-
-static const bool SSL_LOG_STATUS = false;
-static const bool SSL_LOG_DATA = false;
-
-static const int SSL_ERROR_MESSAGE_BUFFER_SIZE = 1000;
-
-const char* commandline_root_certs_file = NULL;
-const char* commandline_root_certs_cache = NULL;
-
-// Get the error messages from BoringSSL, and put them in buffer as a
-// null-terminated string.
-static void FetchErrorString(const SSL* ssl, TextBuffer* text_buffer) {
- const char* sep = File::PathSeparator();
- while (true) {
- const char* path = NULL;
- int line = -1;
- uint32_t error = ERR_get_error_line(&path, &line);
- if (error == 0) {
- break;
- }
- text_buffer->Printf("\n\t%s", ERR_reason_error_string(error));
- if ((ssl != NULL) && (ERR_GET_LIB(error) == ERR_LIB_SSL) &&
- (ERR_GET_REASON(error) == SSL_R_CERTIFICATE_VERIFY_FAILED)) {
- intptr_t result = SSL_get_verify_result(ssl);
- text_buffer->Printf(": %s", X509_verify_cert_error_string(result));
- }
- if ((path != NULL) && (line >= 0)) {
- const char* file = strrchr(path, sep[0]);
- path = file ? file + 1 : path;
- text_buffer->Printf("(%s:%d)", path, line);
- }
- }
-}
-
-
-// Handle an error reported from the BoringSSL library.
-static void ThrowIOException(int status,
- const char* exception_type,
- const char* message,
- const SSL* ssl) {
- Dart_Handle exception;
- {
- TextBuffer error_string(SSL_ERROR_MESSAGE_BUFFER_SIZE);
- FetchErrorString(ssl, &error_string);
- OSError os_error_struct(status, error_string.buf(), OSError::kBoringSSL);
- Dart_Handle os_error = DartUtils::NewDartOSError(&os_error_struct);
- exception =
- DartUtils::NewDartIOException(exception_type, message, os_error);
- ASSERT(!Dart_IsError(exception));
- }
- Dart_ThrowException(exception);
- UNREACHABLE();
-}
-
-
-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, 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, kSSLFilterNativeFieldIndex,
- reinterpret_cast<intptr_t>(filter));
- RETURN_IF_ERROR(err);
- Dart_NewWeakPersistentHandle(dart_this, reinterpret_cast<void*>(filter),
- SSLFilter::kApproximateSize, DeleteFilter);
- return Dart_Null();
-}
-
-
-static SSLContext* GetSecurityContext(Dart_NativeArguments args) {
- SSLContext* context;
- Dart_Handle dart_this = ThrowIfError(Dart_GetNativeArgument(args, 0));
- ASSERT(Dart_IsInstance(dart_this));
- ThrowIfError(
- Dart_GetNativeInstanceField(dart_this, kSecurityContextNativeFieldIndex,
- reinterpret_cast<intptr_t*>(&context)));
- return context;
-}
-
-
-static void DeleteSecurityContext(void* isolate_data,
- Dart_WeakPersistentHandle handle,
- void* context_pointer) {
- SSLContext* context = static_cast<SSLContext*>(context_pointer);
- delete context;
-}
-
-
-static Dart_Handle SetSecurityContext(Dart_NativeArguments args,
- SSLContext* context) {
- 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, kSecurityContextNativeFieldIndex,
- reinterpret_cast<intptr_t>(context));
- RETURN_IF_ERROR(err);
- Dart_NewWeakPersistentHandle(dart_this, context, SSLContext::kApproximateSize,
- DeleteSecurityContext);
- return Dart_Null();
-}
-
-
-static X509* GetX509Certificate(Dart_NativeArguments args) {
- X509* certificate;
- Dart_Handle dart_this = ThrowIfError(Dart_GetNativeArgument(args, 0));
- ASSERT(Dart_IsInstance(dart_this));
- ThrowIfError(
- Dart_GetNativeInstanceField(dart_this, kX509NativeFieldIndex,
- reinterpret_cast<intptr_t*>(&certificate)));
- return certificate;
-}
-
-
-// Forward declaration.
-static void SetAlpnProtocolList(Dart_Handle protocols_handle,
- SSL* ssl,
- SSLContext* context,
- bool is_server);
-
-
-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));
-
- SSLContext* context = NULL;
- if (!Dart_IsNull(context_object)) {
- ThrowIfError(Dart_GetNativeInstanceField(
- context_object, 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->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));
-}
-
-
-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.
-static Dart_Handle 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, 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;
-}
-
-
-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] = 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(SecurityContext_Allocate)(Dart_NativeArguments args) {
- SSLFilter::InitializeLibrary();
- SSL_CTX* ctx = SSL_CTX_new(TLS_method());
- SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, CertificateCallback);
- SSL_CTX_set_min_proto_version(ctx, TLS1_VERSION);
- SSL_CTX_set_cipher_list(ctx, "HIGH:MEDIUM");
- SSLContext* context = new SSLContext(ctx);
- Dart_Handle err = SetSecurityContext(args, context);
- if (Dart_IsError(err)) {
- delete context;
- Dart_PropagateError(err);
- }
-}
-
-
-int PasswordCallback(char* buf, int size, int rwflag, void* userdata) {
- char* password = static_cast<char*>(userdata);
- ASSERT(size == PEM_BUFSIZE);
- strncpy(buf, password, size);
- return strlen(password);
-}
-
-
-void CheckStatusSSL(int status,
- const char* type,
- const char* message,
- const SSL* ssl) {
- // TODO(24183): Take appropriate action on failed calls,
- // throw exception that includes all messages from the error stack.
- if (status == 1) {
- return;
- }
- if (SSL_LOG_STATUS) {
- int error = ERR_get_error();
- Log::PrintErr("Failed: %s status %d", message, status);
- char error_string[SSL_ERROR_MESSAGE_BUFFER_SIZE];
- ERR_error_string_n(error, error_string, SSL_ERROR_MESSAGE_BUFFER_SIZE);
- Log::PrintErr("ERROR: %d %s\n", error, error_string);
- }
- ThrowIOException(status, type, message, ssl);
-}
-
-
-void CheckStatus(int status, const char* type, const char* message) {
- CheckStatusSSL(status, type, message, NULL);
-}
-
-
-// Where the argument to the constructor is the handle for an object
-// implementing List<int>, this class creates a scope in which a memory-backed
-// BIO is allocated. Leaving the scope cleans up the BIO and the buffer that
-// was used to create it.
-//
-// Do not make Dart_ API calls while in a ScopedMemBIO.
-// Do not call Dart_PropagateError while in a ScopedMemBIO.
-class ScopedMemBIO {
- public:
- explicit ScopedMemBIO(Dart_Handle object) {
- if (!Dart_IsTypedData(object) && !Dart_IsList(object)) {
- Dart_ThrowException(
- DartUtils::NewDartArgumentError("Argument is not a List<int>"));
- }
-
- uint8_t* bytes = NULL;
- intptr_t bytes_len = 0;
- bool is_typed_data = false;
- if (Dart_IsTypedData(object)) {
- is_typed_data = true;
- Dart_TypedData_Type typ;
- ThrowIfError(Dart_TypedDataAcquireData(
- object, &typ, reinterpret_cast<void**>(&bytes), &bytes_len));
- } else {
- ASSERT(Dart_IsList(object));
- ThrowIfError(Dart_ListLength(object, &bytes_len));
- bytes = Dart_ScopeAllocate(bytes_len);
- ASSERT(bytes != NULL);
- ThrowIfError(Dart_ListGetAsBytes(object, 0, bytes, bytes_len));
- }
-
- object_ = object;
- bytes_ = bytes;
- bytes_len_ = bytes_len;
- bio_ = BIO_new_mem_buf(bytes, bytes_len);
- ASSERT(bio_ != NULL);
- is_typed_data_ = is_typed_data;
- }
-
- ~ScopedMemBIO() {
- ASSERT(bio_ != NULL);
- if (is_typed_data_) {
- BIO_free(bio_);
- ThrowIfError(Dart_TypedDataReleaseData(object_));
- } else {
- BIO_free(bio_);
- }
- }
-
- BIO* bio() {
- ASSERT(bio_ != NULL);
- return bio_;
- }
-
- private:
- Dart_Handle object_;
- uint8_t* bytes_;
- intptr_t bytes_len_;
- BIO* bio_;
- bool is_typed_data_;
-
- DISALLOW_ALLOCATION();
- DISALLOW_COPY_AND_ASSIGN(ScopedMemBIO);
-};
-
-template <typename T, void (*free_func)(T*)>
-class ScopedSSLType {
- public:
- explicit ScopedSSLType(T* obj) : obj_(obj) {}
-
- ~ScopedSSLType() {
- if (obj_ != NULL) {
- free_func(obj_);
- }
- }
-
- T* get() { return obj_; }
- const T* get() const { return obj_; }
-
- T* release() {
- T* result = obj_;
- obj_ = NULL;
- return result;
- }
-
- private:
- T* obj_;
-
- DISALLOW_ALLOCATION();
- DISALLOW_COPY_AND_ASSIGN(ScopedSSLType);
-};
-
-template <typename T, typename E, void (*func)(E*)>
-class ScopedSSLStackType {
- public:
- explicit ScopedSSLStackType(T* obj) : obj_(obj) {}
-
- ~ScopedSSLStackType() {
- if (obj_ != NULL) {
- sk_pop_free(reinterpret_cast<_STACK*>(obj_),
- reinterpret_cast<void (*)(void*)>(func));
- }
- }
-
- T* get() { return obj_; }
- const T* get() const { return obj_; }
-
- T* release() {
- T* result = obj_;
- obj_ = NULL;
- return result;
- }
-
- private:
- T* obj_;
-
- DISALLOW_ALLOCATION();
- DISALLOW_COPY_AND_ASSIGN(ScopedSSLStackType);
-};
-
-typedef ScopedSSLType<PKCS12, PKCS12_free> ScopedPKCS12;
-typedef ScopedSSLType<X509, X509_free> ScopedX509;
-typedef ScopedSSLStackType<STACK_OF(X509), X509, X509_free> ScopedX509Stack;
-
-static bool NoPEMStartLine() {
- uint32_t last_error = ERR_peek_last_error();
- return (ERR_GET_LIB(last_error) == ERR_LIB_PEM) &&
- (ERR_GET_REASON(last_error) == PEM_R_NO_START_LINE);
-}
-
-
-static EVP_PKEY* GetPrivateKeyPKCS12(BIO* bio, const char* password) {
- ScopedPKCS12 p12(d2i_PKCS12_bio(bio, NULL));
- if (p12.get() == NULL) {
- return NULL;
- }
-
- EVP_PKEY* key = NULL;
- X509* cert = NULL;
- STACK_OF(X509)* ca_certs = NULL;
- int status = PKCS12_parse(p12.get(), password, &key, &cert, &ca_certs);
- if (status == 0) {
- return NULL;
- }
-
- // We only care about the private key.
- ScopedX509 delete_cert(cert);
- ScopedX509Stack delete_ca_certs(ca_certs);
- return key;
-}
-
-
-static EVP_PKEY* GetPrivateKey(BIO* bio, const char* password) {
- EVP_PKEY* key = PEM_read_bio_PrivateKey(bio, NULL, PasswordCallback,
- const_cast<char*>(password));
- if (key == NULL) {
- // We try reading data as PKCS12 only if reading as PEM was unsuccessful and
- // if there is no indication that the data is malformed PEM. We assume the
- // data is malformed PEM if it contains the start line, i.e. a line
- // with ----- BEGIN.
- if (NoPEMStartLine()) {
- // Reset the bio, and clear the error from trying to read as PEM.
- ERR_clear_error();
- BIO_reset(bio);
-
- // Try to decode as PKCS12.
- key = GetPrivateKeyPKCS12(bio, password);
- }
- }
- return key;
-}
-
-
-static const char* GetPasswordArgument(Dart_NativeArguments args,
- intptr_t index) {
- Dart_Handle password_object =
- ThrowIfError(Dart_GetNativeArgument(args, index));
- const char* password = NULL;
- if (Dart_IsString(password_object)) {
- ThrowIfError(Dart_StringToCString(password_object, &password));
- if (strlen(password) > PEM_BUFSIZE - 1) {
- Dart_ThrowException(DartUtils::NewDartArgumentError(
- "Password length is greater than 1023 (PEM_BUFSIZE)"));
- }
- } else if (Dart_IsNull(password_object)) {
- password = "";
- } else {
- Dart_ThrowException(
- DartUtils::NewDartArgumentError("Password is not a String or null"));
- }
- return password;
-}
-
-
-void FUNCTION_NAME(SecurityContext_UsePrivateKeyBytes)(
- Dart_NativeArguments args) {
- SSLContext* context = GetSecurityContext(args);
- const char* password = GetPasswordArgument(args, 2);
-
- int status;
- {
- ScopedMemBIO bio(ThrowIfError(Dart_GetNativeArgument(args, 1)));
- EVP_PKEY* key = GetPrivateKey(bio.bio(), password);
- status = SSL_CTX_use_PrivateKey(context->context(), key);
- // SSL_CTX_use_PrivateKey increments the reference count of key on success,
- // so we have to call EVP_PKEY_free on both success and failure.
- EVP_PKEY_free(key);
- }
-
- // TODO(24184): Handle different expected errors here - file missing,
- // incorrect password, file not a PEM, and throw exceptions.
- // CheckStatus should also throw an exception in uncaught cases.
- CheckStatus(status, "TlsException", "Failure in usePrivateKeyBytes");
-}
-
-
-static int SetTrustedCertificatesBytesPKCS12(SSL_CTX* context,
- BIO* bio,
- const char* password) {
- ScopedPKCS12 p12(d2i_PKCS12_bio(bio, NULL));
- if (p12.get() == NULL) {
- return 0;
- }
-
- EVP_PKEY* key = NULL;
- X509* cert = NULL;
- STACK_OF(X509)* ca_certs = NULL;
- int status = PKCS12_parse(p12.get(), password, &key, &cert, &ca_certs);
- if (status == 0) {
- return status;
- }
-
- ScopedX509Stack cert_stack(ca_certs);
- X509_STORE* store = SSL_CTX_get_cert_store(context);
- status = X509_STORE_add_cert(store, cert);
- // X509_STORE_add_cert increments the reference count of cert on success.
- X509_free(cert);
- if (status == 0) {
- return status;
- }
-
- X509* ca;
- while ((ca = sk_X509_shift(cert_stack.get())) != NULL) {
- status = X509_STORE_add_cert(store, ca);
- // X509_STORE_add_cert increments the reference count of cert on success.
- X509_free(ca);
- if (status == 0) {
- return status;
- }
- }
-
- return status;
-}
-
-
-static int SetTrustedCertificatesBytesPEM(SSL_CTX* context, BIO* bio) {
- X509_STORE* store = SSL_CTX_get_cert_store(context);
-
- int status = 0;
- X509* cert = NULL;
- while ((cert = PEM_read_bio_X509(bio, NULL, NULL, NULL)) != NULL) {
- status = X509_STORE_add_cert(store, cert);
- // X509_STORE_add_cert increments the reference count of cert on success.
- X509_free(cert);
- if (status == 0) {
- return status;
- }
- }
-
- // If no PEM start line is found, it means that we read to the end of the
- // file, or that the file isn't PEM. In the first case, status will be
- // non-zero indicating success. In the second case, status will be 0,
- // indicating that we should try to read as PKCS12. If there is some other
- // error, we return it up to the caller.
- return NoPEMStartLine() ? status : 0;
-}
-
-
-static int SetTrustedCertificatesBytes(SSL_CTX* context,
- BIO* bio,
- const char* password) {
- int status = SetTrustedCertificatesBytesPEM(context, bio);
- if (status == 0) {
- if (NoPEMStartLine()) {
- ERR_clear_error();
- BIO_reset(bio);
- status = SetTrustedCertificatesBytesPKCS12(context, bio, password);
- }
- } else {
- // The PEM file was successfully parsed.
- ERR_clear_error();
- }
- return status;
-}
-
-
-void FUNCTION_NAME(SecurityContext_SetTrustedCertificatesBytes)(
- Dart_NativeArguments args) {
- SSLContext* context = GetSecurityContext(args);
- const char* password = GetPasswordArgument(args, 2);
- int status;
- {
- ScopedMemBIO bio(ThrowIfError(Dart_GetNativeArgument(args, 1)));
- status =
- SetTrustedCertificatesBytes(context->context(), bio.bio(), password);
- }
- CheckStatus(status, "TlsException", "Failure in setTrustedCertificatesBytes");
-}
-
-
-void FUNCTION_NAME(SecurityContext_AlpnSupported)(Dart_NativeArguments args) {
- Dart_SetReturnValue(args, Dart_NewBoolean(true));
-}
-
-
-static void AddCompiledInCerts(SSLContext* context) {
- if (root_certificates_pem == NULL) {
- if (SSL_LOG_STATUS) {
- Log::Print("Missing compiled-in roots\n");
- }
- return;
- }
- X509_STORE* store = SSL_CTX_get_cert_store(context->context());
- BIO* roots_bio =
- BIO_new_mem_buf(const_cast<unsigned char*>(root_certificates_pem),
- root_certificates_pem_length);
- X509* root_cert;
- // PEM_read_bio_X509 reads PEM-encoded certificates from a bio (in our case,
- // backed by a memory buffer), and returns X509 objects, one by one.
- // When the end of the bio is reached, it returns null.
- while ((root_cert = PEM_read_bio_X509(roots_bio, NULL, NULL, NULL)) != NULL) {
- int status = X509_STORE_add_cert(store, root_cert);
- // X509_STORE_add_cert increments the reference count of cert on success.
- X509_free(root_cert);
- if (status == 0) {
- break;
- }
- }
- BIO_free(roots_bio);
- // If there is an error here, it must be the error indicating that we are done
- // reading PEM certificates.
- ASSERT((ERR_peek_error() == 0) || NoPEMStartLine());
- ERR_clear_error();
-}
-
-
-static void LoadRootCertFile(SSLContext* context, const char* file) {
- if (SSL_LOG_STATUS) {
- Log::Print("Looking for trusted roots in %s\n", file);
- }
- if (!File::Exists(file)) {
- ThrowIOException(-1, "TlsException", "Failed to find root cert file", NULL);
- }
- int status = SSL_CTX_load_verify_locations(context->context(), file, NULL);
- CheckStatus(status, "TlsException", "Failure trusting builtin roots");
- if (SSL_LOG_STATUS) {
- Log::Print("Trusting roots from: %s\n", file);
- }
-}
-
-
-static void LoadRootCertCache(SSLContext* context, const char* cache) {
- if (SSL_LOG_STATUS) {
- Log::Print("Looking for trusted roots in %s\n", cache);
- }
- if (Directory::Exists(cache) != Directory::EXISTS) {
- ThrowIOException(-1, "TlsException", "Failed to find root cert cache",
- NULL);
- }
- int status = SSL_CTX_load_verify_locations(context->context(), NULL, cache);
- CheckStatus(status, "TlsException", "Failure trusting builtin roots");
- if (SSL_LOG_STATUS) {
- Log::Print("Trusting roots from: %s\n", cache);
- }
-}
-
-
-void FUNCTION_NAME(SecurityContext_TrustBuiltinRoots)(
- Dart_NativeArguments args) {
- SSLContext* context = GetSecurityContext(args);
-
- // First, try to use locations specified on the command line.
- if (commandline_root_certs_file != NULL) {
- LoadRootCertFile(context, commandline_root_certs_file);
- return;
- }
-
- if (commandline_root_certs_cache != NULL) {
- LoadRootCertCache(context, commandline_root_certs_cache);
- return;
- }
-
-#if defined(HOST_OS_ANDROID)
- // On Android, we don't compile in the trusted root certificates. Insead,
- // we use the directory of trusted certificates already present on the device.
- // This saves ~240KB from the size of the binary. This has the drawback that
- // SSL_do_handshake will synchronously hit the filesystem looking for root
- // certs during its trust evaluation. We call SSL_do_handshake directly from
- // the Dart thread so that Dart code can be invoked from the "bad certificate"
- // callback called by SSL_do_handshake.
- const char* android_cacerts = "/system/etc/security/cacerts";
- LoadRootCertCache(context, android_cacerts);
- return;
-#elif defined(HOST_OS_LINUX)
- // On Linux, we use the compiled-in trusted certs as a last resort. First,
- // we try to find the trusted certs in various standard locations. A good
- // discussion of the complexities of this endeavor can be found here:
- //
- // https://www.happyassassin.net/2015/01/12/a-note-about-ssltls-trusted-certificate-stores-and-platforms/
- const char* bundle = "/etc/pki/tls/certs/ca-bundle.crt";
- const char* cachedir = "/etc/ssl/certs";
- if (File::Exists(bundle)) {
- LoadRootCertFile(context, bundle);
- return;
- }
-
- if (Directory::Exists(cachedir) == Directory::EXISTS) {
- LoadRootCertCache(context, cachedir);
- return;
- }
-#endif // defined(HOST_OS_ANDROID)
-
- // Fall back on the compiled-in certs if the standard locations don't exist,
- // or we aren't on Linux.
- if (SSL_LOG_STATUS) {
- Log::Print("Trusting compiled-in roots\n");
- }
- AddCompiledInCerts(context);
-}
-
-
-static int UseChainBytesPKCS12(SSL_CTX* context,
- BIO* bio,
- const char* password) {
- ScopedPKCS12 p12(d2i_PKCS12_bio(bio, NULL));
- if (p12.get() == NULL) {
- return 0;
- }
-
- EVP_PKEY* key = NULL;
- X509* cert = NULL;
- STACK_OF(X509)* ca_certs = NULL;
- int status = PKCS12_parse(p12.get(), password, &key, &cert, &ca_certs);
- if (status == 0) {
- return status;
- }
-
- ScopedX509 x509(cert);
- ScopedX509Stack certs(ca_certs);
- status = SSL_CTX_use_certificate(context, x509.get());
- if (ERR_peek_error() != 0) {
- // Key/certificate mismatch doesn't imply status is 0.
- status = 0;
- }
- if (status == 0) {
- return status;
- }
-
- SSL_CTX_clear_chain_certs(context);
-
- X509* ca;
- while ((ca = sk_X509_shift(certs.get())) != NULL) {
- status = SSL_CTX_add0_chain_cert(context, ca);
- // SSL_CTX_add0_chain_cert does not inc ref count, so don't free unless the
- // call fails.
- if (status == 0) {
- X509_free(ca);
- return status;
- }
- }
-
- return status;
-}
-
-
-static int UseChainBytesPEM(SSL_CTX* context, BIO* bio) {
- int status = 0;
- ScopedX509 x509(PEM_read_bio_X509_AUX(bio, NULL, NULL, NULL));
- if (x509.get() == NULL) {
- return 0;
- }
-
- status = SSL_CTX_use_certificate(context, x509.get());
- if (ERR_peek_error() != 0) {
- // Key/certificate mismatch doesn't imply status is 0.
- status = 0;
- }
- if (status == 0) {
- return status;
- }
-
- SSL_CTX_clear_chain_certs(context);
-
- X509* ca;
- while ((ca = PEM_read_bio_X509(bio, NULL, NULL, NULL)) != NULL) {
- status = SSL_CTX_add0_chain_cert(context, ca);
- // SSL_CTX_add0_chain_cert does not inc ref count, so don't free unless the
- // call fails.
- if (status == 0) {
- X509_free(ca);
- return status;
- }
- // Note that we must not free `ca` if it was successfully added to the
- // chain. We must free the main certificate x509, though since its reference
- // count is increased by SSL_CTX_use_certificate.
- }
-
- return NoPEMStartLine() ? status : 0;
-}
-
-
-static int UseChainBytes(SSL_CTX* context, BIO* bio, const char* password) {
- int status = UseChainBytesPEM(context, bio);
- if (status == 0) {
- if (NoPEMStartLine()) {
- ERR_clear_error();
- BIO_reset(bio);
- status = UseChainBytesPKCS12(context, bio, password);
- }
- } else {
- // The PEM file was successfully read.
- ERR_clear_error();
- }
- return status;
-}
-
-
-void FUNCTION_NAME(SecurityContext_UseCertificateChainBytes)(
- Dart_NativeArguments args) {
- SSLContext* context = GetSecurityContext(args);
- const char* password = GetPasswordArgument(args, 2);
- int status;
- {
- ScopedMemBIO bio(ThrowIfError(Dart_GetNativeArgument(args, 1)));
- status = UseChainBytes(context->context(), bio.bio(), password);
- }
- CheckStatus(status, "TlsException", "Failure in useCertificateChainBytes");
-}
-
-
-static int SetClientAuthoritiesPKCS12(SSL_CTX* context,
- BIO* bio,
- const char* password) {
- ScopedPKCS12 p12(d2i_PKCS12_bio(bio, NULL));
- if (p12.get() == NULL) {
- return 0;
- }
-
- EVP_PKEY* key = NULL;
- X509* cert = NULL;
- STACK_OF(X509)* ca_certs = NULL;
- int status = PKCS12_parse(p12.get(), password, &key, &cert, &ca_certs);
- if (status == 0) {
- return status;
- }
-
- ScopedX509Stack cert_stack(ca_certs);
- status = SSL_CTX_add_client_CA(context, cert);
- // SSL_CTX_add_client_CA increments the reference count of cert on success.
- X509_free(cert);
- if (status == 0) {
- return status;
- }
-
- X509* ca;
- while ((ca = sk_X509_shift(cert_stack.get())) != NULL) {
- status = SSL_CTX_add_client_CA(context, ca);
- // SSL_CTX_add_client_CA increments the reference count of ca on success.
- X509_free(ca); // The name has been extracted.
- if (status == 0) {
- return status;
- }
- }
-
- return status;
-}
-
-
-static int SetClientAuthoritiesPEM(SSL_CTX* context, BIO* bio) {
- int status = 0;
- X509* cert = NULL;
- while ((cert = PEM_read_bio_X509(bio, NULL, NULL, NULL)) != NULL) {
- status = SSL_CTX_add_client_CA(context, cert);
- X509_free(cert); // The name has been extracted.
- if (status == 0) {
- return status;
- }
- }
- return NoPEMStartLine() ? status : 0;
-}
-
-
-static int SetClientAuthorities(SSL_CTX* context,
- BIO* bio,
- const char* password) {
- int status = SetClientAuthoritiesPEM(context, bio);
- if (status == 0) {
- if (NoPEMStartLine()) {
- ERR_clear_error();
- BIO_reset(bio);
- status = SetClientAuthoritiesPKCS12(context, bio, password);
- }
- } else {
- // The PEM file was successfully parsed.
- ERR_clear_error();
- }
- return status;
-}
-
-
-void FUNCTION_NAME(SecurityContext_SetClientAuthoritiesBytes)(
- Dart_NativeArguments args) {
- SSLContext* context = GetSecurityContext(args);
- const char* password = GetPasswordArgument(args, 2);
-
- int status;
- {
- ScopedMemBIO bio(ThrowIfError(Dart_GetNativeArgument(args, 1)));
- status = SetClientAuthorities(context->context(), bio.bio(), password);
- }
-
- CheckStatus(status, "TlsException", "Failure in setClientAuthoritiesBytes");
-}
-
-
-void FUNCTION_NAME(SecurityContext_SetAlpnProtocols)(
- Dart_NativeArguments args) {
- SSLContext* context = GetSecurityContext(args);
- Dart_Handle protocols_handle = ThrowIfError(Dart_GetNativeArgument(args, 1));
- Dart_Handle is_server_handle = ThrowIfError(Dart_GetNativeArgument(args, 2));
- if (Dart_IsBoolean(is_server_handle)) {
- bool is_server = DartUtils::GetBooleanValue(is_server_handle);
- SetAlpnProtocolList(protocols_handle, NULL, context, is_server);
- } else {
- Dart_ThrowException(DartUtils::NewDartArgumentError(
- "Non-boolean is_server argument passed to SetAlpnProtocols"));
- }
-}
-
-
-void FUNCTION_NAME(X509_Subject)(Dart_NativeArguments args) {
- X509* certificate = GetX509Certificate(args);
- X509_NAME* subject = X509_get_subject_name(certificate);
- char* subject_string = X509_NAME_oneline(subject, NULL, 0);
- Dart_SetReturnValue(args, Dart_NewStringFromCString(subject_string));
- OPENSSL_free(subject_string);
-}
-
-
-void FUNCTION_NAME(X509_Issuer)(Dart_NativeArguments args) {
- X509* certificate = GetX509Certificate(args);
- X509_NAME* issuer = X509_get_issuer_name(certificate);
- char* issuer_string = X509_NAME_oneline(issuer, NULL, 0);
- Dart_SetReturnValue(args, Dart_NewStringFromCString(issuer_string));
- OPENSSL_free(issuer_string);
-}
-
-static Dart_Handle ASN1TimeToMilliseconds(ASN1_TIME* aTime) {
- ASN1_UTCTIME* epoch_start = M_ASN1_UTCTIME_new();
- ASN1_UTCTIME_set_string(epoch_start, "700101000000Z");
- int days;
- int seconds;
- int result = ASN1_TIME_diff(&days, &seconds, epoch_start, aTime);
- M_ASN1_UTCTIME_free(epoch_start);
- if (result != 1) {
- // TODO(whesse): Propagate an error to Dart.
- Log::PrintErr("ASN1Time error %d\n", result);
- }
- return Dart_NewInteger((86400LL * days + seconds) * 1000LL);
-}
-
-void FUNCTION_NAME(X509_StartValidity)(Dart_NativeArguments args) {
- X509* certificate = GetX509Certificate(args);
- ASN1_TIME* not_before = X509_get_notBefore(certificate);
- Dart_SetReturnValue(args, ASN1TimeToMilliseconds(not_before));
-}
-
-
-void FUNCTION_NAME(X509_EndValidity)(Dart_NativeArguments args) {
- X509* certificate = GetX509Certificate(args);
- ASN1_TIME* not_after = X509_get_notAfter(certificate);
- Dart_SetReturnValue(args, ASN1TimeToMilliseconds(not_after));
-}
-
-
-/**
- * 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(SSL_ERROR_MESSAGE_BUFFER_SIZE);
- 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);
-}
-
-
-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;
- }
-}
-
-
-Dart_Handle SSLFilter::PeerCertificate() {
- // SSL_get_peer_certificate incs the refcount of certificate. X509_free is
- // called by the finalizer set up by WrappedX509Certificate.
- X509* certificate = SSL_get_peer_certificate(ssl_);
- return WrappedX509Certificate(certificate);
-}
-
-
-int AlpnCallback(SSL* ssl,
- const uint8_t** out,
- uint8_t* outlen,
- const uint8_t* in,
- unsigned int inlen,
- void* arg) {
- // 'in' and 'arg' are sequences of (length, data) strings with 1-byte lengths.
- // 'arg' is 0-terminated. Finds the first string in 'arg' that is in 'in'.
- uint8_t* server_list = static_cast<uint8_t*>(arg);
- while (*server_list != 0) {
- uint8_t protocol_length = *server_list++;
- const uint8_t* client_list = in;
- while (client_list < in + inlen) {
- uint8_t client_protocol_length = *client_list++;
- if (client_protocol_length == protocol_length) {
- if (0 == memcmp(server_list, client_list, protocol_length)) {
- *out = client_list;
- *outlen = client_protocol_length;
- return SSL_TLSEXT_ERR_OK; // Success
- }
- }
- client_list += client_protocol_length;
- }
- server_list += protocol_length;
- }
- // TODO(23580): Make failure send a fatal alert instead of ignoring ALPN.
- return SSL_TLSEXT_ERR_NOACK;
-}
-
-
-// Sets the protocol list for ALPN on a SSL object or a context.
-static void SetAlpnProtocolList(Dart_Handle protocols_handle,
- SSL* ssl,
- SSLContext* context,
- bool is_server) {
- // Enable ALPN (application layer protocol negotiation) if the caller provides
- // a valid list of supported protocols.
- Dart_TypedData_Type protocols_type;
- uint8_t* protocol_string = NULL;
- uint8_t* protocol_string_copy = NULL;
- intptr_t protocol_string_len = 0;
- int status;
-
- Dart_Handle result = Dart_TypedDataAcquireData(
- protocols_handle, &protocols_type,
- reinterpret_cast<void**>(&protocol_string), &protocol_string_len);
- if (Dart_IsError(result)) {
- Dart_PropagateError(result);
- }
-
- if (protocols_type != Dart_TypedData_kUint8) {
- Dart_TypedDataReleaseData(protocols_handle);
- Dart_PropagateError(Dart_NewApiError(
- "Unexpected type for protocols (expected valid Uint8List)."));
- }
-
- if (protocol_string_len > 0) {
- if (is_server) {
- // ALPN on server connections must be set on an SSL_CTX object,
- // not on the SSL object of the individual connection.
- ASSERT(context != NULL);
- ASSERT(ssl == NULL);
- // Because it must be passed as a single void*, terminate
- // the list of (length, data) strings with a length 0 string.
- protocol_string_copy =
- static_cast<uint8_t*>(malloc(protocol_string_len + 1));
- memmove(protocol_string_copy, protocol_string, protocol_string_len);
- protocol_string_copy[protocol_string_len] = '\0';
- SSL_CTX_set_alpn_select_cb(context->context(), AlpnCallback,
- protocol_string_copy);
- context->set_alpn_protocol_string(protocol_string_copy);
- } else {
- // The function makes a local copy of protocol_string, which it owns.
- if (ssl != NULL) {
- ASSERT(context == NULL);
- status = SSL_set_alpn_protos(ssl, protocol_string, protocol_string_len);
- } else {
- ASSERT(context != NULL);
- ASSERT(ssl == NULL);
- status = SSL_CTX_set_alpn_protos(context->context(), protocol_string,
- protocol_string_len);
- }
- ASSERT(status == 0); // The function returns a non-standard status.
- }
- }
- Dart_TypedDataReleaseData(protocols_handle);
-}
-
-
-void SSLFilter::Connect(const char* hostname,
- SSL_CTX* 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);
- CheckStatusSSL(status, "TlsException", "BIO_new_bio_pair", ssl_);
-
- assert(context != NULL);
- ssl_ = SSL_new(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);
-
- 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 {
- SetAlpnProtocolList(protocols_handle, ssl_, NULL, false);
- status = SSL_set_tlsext_host_name(ssl_, hostname);
- 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_));
- 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) {
- 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;
- }
- 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.
-}
-
-
-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(HOST_OS_LINUX)
-
-#endif // !defined(DART_IO_DISABLED) &&
- // !defined(DART_IO_SECURE_SOCKET_DISABLED)
diff --git a/runtime/bin/secure_socket_filter.cc b/runtime/bin/secure_socket_filter.cc
new file mode 100644
index 0000000..c07eaad
--- /dev/null
+++ b/runtime/bin/secure_socket_filter.cc
@@ -0,0 +1,745 @@
+// 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 "bin/lockers.h"
+#include "bin/log.h"
+#include "bin/secure_socket_utils.h"
+#include "bin/security_context.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();
+}
+
+
+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 X509Helper::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);
+ context->RegisterCallbacks(ssl_);
+
+ 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();
+}
+
+
+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.
+}
+
+
+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)
diff --git a/runtime/bin/secure_socket_boringssl.h b/runtime/bin/secure_socket_filter.h
similarity index 66%
rename from runtime/bin/secure_socket_boringssl.h
rename to runtime/bin/secure_socket_filter.h
index e14171d..dda69eb 100644
--- a/runtime/bin/secure_socket_boringssl.h
+++ b/runtime/bin/secure_socket_filter.h
@@ -1,30 +1,18 @@
-// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
+// 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.
-#ifndef RUNTIME_BIN_SECURE_SOCKET_BORINGSSL_H_
-#define RUNTIME_BIN_SECURE_SOCKET_BORINGSSL_H_
-
-#if !defined(RUNTIME_BIN_SECURE_SOCKET_H_)
-#error Do not include secure_socket_boringssl.h directly. Use secure_socket.h.
-#endif
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
+#ifndef RUNTIME_BIN_SECURE_SOCKET_FILTER_H_
+#define RUNTIME_BIN_SECURE_SOCKET_FILTER_H_
#include <openssl/bio.h>
-#include <openssl/err.h>
#include <openssl/ssl.h>
#include <openssl/x509.h>
#include "bin/builtin.h"
-#include "bin/dartutils.h"
#include "bin/reference_counting.h"
-#include "bin/socket.h"
-#include "bin/thread.h"
-#include "bin/utils.h"
+#include "bin/security_context.h"
+#include "platform/utils.h"
namespace dart {
namespace bin {
@@ -33,44 +21,6 @@
extern const unsigned char* root_certificates_pem;
extern unsigned int root_certificates_pem_length;
-class SSLContext {
- public:
- static const intptr_t kApproximateSize;
-
- explicit SSLContext(SSL_CTX* context)
- : context_(context), alpn_protocol_string_(NULL) {}
-
- ~SSLContext() {
- SSL_CTX_free(context_);
- if (alpn_protocol_string_ != NULL) {
- free(alpn_protocol_string_);
- }
- }
-
- SSL_CTX* context() const { return context_; }
-
- uint8_t* alpn_protocol_string() const { return alpn_protocol_string_; }
- void set_alpn_protocol_string(uint8_t* protocol_string) {
- if (alpn_protocol_string_ != NULL) {
- free(alpn_protocol_string_);
- }
- alpn_protocol_string_ = protocol_string;
- }
-
- private:
- SSL_CTX* context_;
- uint8_t* alpn_protocol_string_;
-
- DISALLOW_COPY_AND_ASSIGN(SSLContext);
-};
-
-/*
- * SSLFilter encapsulates the SSL(TLS) code in a filter, that communicates
- * with the containing _SecureFilterImpl Dart object through four shared
- * ExternalByteArray buffers, for reading and writing plaintext, and
- * reading and writing encrypted text. The filter handles handshaking
- * and certificate verification.
- */
class SSLFilter : public ReferenceCounted<SSLFilter> {
public:
// These enums must agree with those in sdk/lib/io/secure_socket.dart.
@@ -84,6 +34,7 @@
};
static const intptr_t kApproximateSize;
+ static const int kSSLFilterNativeFieldIndex = 0;
SSLFilter()
: callback_error(NULL),
@@ -100,7 +51,7 @@
Dart_Handle Init(Dart_Handle dart_this);
void Connect(const char* hostname,
- SSL_CTX* context,
+ SSLCertContext* context,
bool is_server,
bool request_client_certificate,
bool require_client_certificate,
@@ -133,15 +84,14 @@
// The index of the external data field in _ssl that points to the SSLFilter.
static int filter_ssl_index;
- // TODO(whesse): make private:
- SSL* ssl_;
- BIO* socket_side_;
-
private:
static const intptr_t kInternalBIOSize;
static bool library_initialized_;
static Mutex* mutex_; // To protect library initialization.
+ SSL* ssl_;
+ BIO* socket_side_;
+
uint8_t* buffers_[kNumBuffers];
int buffer_size_;
int encrypted_buffer_size_;
@@ -154,7 +104,7 @@
bool is_server_;
char* hostname_;
- static bool isBufferEncrypted(int i) {
+ static bool IsBufferEncrypted(int i) {
return static_cast<BufferIndex>(i) >= kFirstEncrypted;
}
Dart_Handle InitializeBuffers(Dart_Handle dart_this);
@@ -166,4 +116,4 @@
} // namespace bin
} // namespace dart
-#endif // RUNTIME_BIN_SECURE_SOCKET_BORINGSSL_H_
+#endif // RUNTIME_BIN_SECURE_SOCKET_FILTER_H_
diff --git a/runtime/bin/secure_socket_ios.cc b/runtime/bin/secure_socket_ios.cc
deleted file mode 100644
index e2d9abc..0000000
--- a/runtime/bin/secure_socket_ios.cc
+++ /dev/null
@@ -1,1507 +0,0 @@
-// Copyright (c) 2016, 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 "platform/globals.h"
-#if HOST_OS_IOS
-
-#include "bin/secure_socket.h"
-#include "bin/secure_socket_ios.h"
-
-#include <errno.h>
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <sys/syslimits.h>
-#include <stdio.h>
-#include <string.h>
-
-#include <CoreFoundation/CoreFoundation.h>
-#include <Security/SecureTransport.h>
-#include <Security/Security.h>
-
-#include "bin/builtin.h"
-#include "bin/dartutils.h"
-#include "bin/lockers.h"
-#include "bin/log.h"
-#include "bin/socket.h"
-#include "bin/thread.h"
-#include "bin/utils.h"
-
-#include "platform/text_buffer.h"
-#include "platform/utils.h"
-
-#include "include/dart_api.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 {
-
-static const int kSSLFilterNativeFieldIndex = 0;
-static const int kSecurityContextNativeFieldIndex = 0;
-static const int kX509NativeFieldIndex = 0;
-
-static const bool SSL_LOG_STATUS = false;
-static const bool SSL_LOG_DATA = false;
-static const bool SSL_LOG_CERTS = false;
-static const int SSL_ERROR_MESSAGE_BUFFER_SIZE = 1000;
-static const intptr_t PEM_BUFSIZE = 1024;
-
-static char* CFStringRefToCString(CFStringRef cfstring) {
- CFIndex len = CFStringGetLength(cfstring);
- CFIndex max_len =
- CFStringGetMaximumSizeForEncoding(len, kCFStringEncodingUTF8) + 1;
- char* result = reinterpret_cast<char*>(Dart_ScopeAllocate(max_len));
- ASSERT(result != NULL);
- bool success =
- CFStringGetCString(cfstring, result, max_len, kCFStringEncodingUTF8);
- return success ? result : NULL;
-}
-
-
-// Handle an error reported from the SecureTransport library.
-static void ThrowIOException(OSStatus status,
- const char* exception_type,
- const char* message) {
- TextBuffer status_message(SSL_ERROR_MESSAGE_BUFFER_SIZE);
- status_message.Printf("OSStatus = %ld: https://www.osstatus.com",
- static_cast<intptr_t>(status));
- OSError os_error_struct(status, status_message.buf(), OSError::kBoringSSL);
- Dart_Handle os_error = DartUtils::NewDartOSError(&os_error_struct);
- Dart_Handle exception =
- DartUtils::NewDartIOException(exception_type, message, os_error);
- ASSERT(!Dart_IsError(exception));
- Dart_ThrowException(exception);
- UNREACHABLE();
-}
-
-
-static void CheckStatus(OSStatus status,
- const char* type,
- const char* message) {
- if (status == noErr) {
- return;
- }
- ThrowIOException(status, type, message);
-}
-
-
-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, 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);
- const int approximate_size_of_filter = 1500;
- 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, kSSLFilterNativeFieldIndex,
- reinterpret_cast<intptr_t>(filter));
- RETURN_IF_ERROR(err);
- Dart_NewWeakPersistentHandle(dart_this, reinterpret_cast<void*>(filter),
- approximate_size_of_filter, DeleteFilter);
- return Dart_Null();
-}
-
-
-static SSLCertContext* GetSecurityContext(Dart_NativeArguments args) {
- SSLCertContext* context;
- Dart_Handle dart_this = ThrowIfError(Dart_GetNativeArgument(args, 0));
- ASSERT(Dart_IsInstance(dart_this));
- ThrowIfError(
- Dart_GetNativeInstanceField(dart_this, kSecurityContextNativeFieldIndex,
- reinterpret_cast<intptr_t*>(&context)));
- return context;
-}
-
-
-static void DeleteCertContext(void* isolate_data,
- Dart_WeakPersistentHandle handle,
- void* context_pointer) {
- SSLCertContext* context = static_cast<SSLCertContext*>(context_pointer);
- context->Release();
-}
-
-
-static Dart_Handle SetSecurityContext(Dart_NativeArguments args,
- SSLCertContext* context) {
- const int approximate_size_of_context = 1500;
- 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, kSecurityContextNativeFieldIndex,
- reinterpret_cast<intptr_t>(context));
- RETURN_IF_ERROR(err);
- Dart_NewWeakPersistentHandle(dart_this, context, approximate_size_of_context,
- DeleteCertContext);
- return Dart_Null();
-}
-
-
-static SecCertificateRef GetX509Certificate(Dart_NativeArguments args) {
- SecCertificateRef certificate;
- Dart_Handle dart_this = ThrowIfError(Dart_GetNativeArgument(args, 0));
- ASSERT(Dart_IsInstance(dart_this));
- ThrowIfError(
- Dart_GetNativeInstanceField(dart_this, kX509NativeFieldIndex,
- reinterpret_cast<intptr_t*>(&certificate)));
- return certificate;
-}
-
-
-static void ReleaseCertificate(void* isolate_data,
- Dart_WeakPersistentHandle handle,
- void* context_pointer) {
- SecCertificateRef cert = reinterpret_cast<SecCertificateRef>(context_pointer);
- CFRelease(cert);
-}
-
-
-static Dart_Handle WrappedX509Certificate(SecCertificateRef certificate) {
- const intptr_t approximate_size_of_certificate = 1500;
- if (certificate == NULL) {
- return Dart_Null();
- }
- Dart_Handle x509_type =
- DartUtils::GetDartType(DartUtils::kIOLibURL, "X509Certificate");
- if (Dart_IsError(x509_type)) {
- return x509_type;
- }
- Dart_Handle arguments[] = {NULL};
-
- Dart_Handle result =
- Dart_New(x509_type, DartUtils::NewString("_"), 0, arguments);
- if (Dart_IsError(result)) {
- return result;
- }
- ASSERT(Dart_IsInstance(result));
-
- // CFRetain in case the returned Dart object outlives the SecurityContext.
- // CFRelease is in the Dart object's finalizer
- CFRetain(certificate);
- Dart_NewWeakPersistentHandle(result, reinterpret_cast<void*>(certificate),
- approximate_size_of_certificate,
- ReleaseCertificate);
-
- Dart_Handle status = Dart_SetNativeInstanceField(
- result, kX509NativeFieldIndex, reinterpret_cast<intptr_t>(certificate));
- if (Dart_IsError(status)) {
- return status;
- }
- return result;
-}
-
-
-static const char* GetPasswordArgument(Dart_NativeArguments args,
- intptr_t index) {
- Dart_Handle password_object =
- ThrowIfError(Dart_GetNativeArgument(args, index));
- const char* password = NULL;
- if (Dart_IsString(password_object)) {
- ThrowIfError(Dart_StringToCString(password_object, &password));
- if (strlen(password) > PEM_BUFSIZE - 1) {
- Dart_ThrowException(DartUtils::NewDartArgumentError(
- "Password length is greater than 1023 bytes."));
- }
- } else if (Dart_IsNull(password_object)) {
- password = "";
- } else {
- Dart_ThrowException(
- DartUtils::NewDartArgumentError("Password is not a String or null"));
- }
- return password;
-}
-
-
-static OSStatus TryPKCS12Import(CFDataRef cfdata,
- CFStringRef password,
- CFArrayRef* out_certs,
- SecIdentityRef* out_identity) {
- const void* keys[] = {kSecImportExportPassphrase};
- const void* values[] = {password};
- CFDictionaryRef params =
- CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);
- CFArrayRef items = NULL;
- OSStatus status = SecPKCS12Import(cfdata, params, &items);
- CFRelease(params);
-
- if (status != noErr) {
- if (SSL_LOG_STATUS) {
- Log::PrintErr("SecPKCS12Import: status = %ld",
- static_cast<intptr_t>(status));
- return status;
- }
- }
-
- CFIndex items_length = (items == NULL) ? 0 : CFArrayGetCount(items);
- if (SSL_LOG_CERTS) {
- Log::PrintErr("TryPKCS12Import succeeded, count = %ld\n", items_length);
- }
-
- // Empty list indicates a decoding failure of some sort.
- if ((items != NULL) && (items_length == 0)) {
- CFRelease(items);
- return errSSLBadCert;
- }
-
- CFMutableArrayRef result_certs =
- CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
- SecIdentityRef result_identity = NULL;
-
- for (CFIndex i = 0; i < items_length; i++) {
- CFTypeRef item =
- reinterpret_cast<CFTypeRef>(CFArrayGetValueAtIndex(items, i));
- ASSERT(CFGetTypeID(item) == CFDictionaryGetTypeID());
- CFDictionaryRef dict = reinterpret_cast<CFDictionaryRef>(item);
-
- // Trust.
- CFTypeRef trust_item = CFDictionaryGetValue(dict, kSecImportItemTrust);
- if (trust_item != NULL) {
- ASSERT(CFGetTypeID(trust_item) == SecTrustGetTypeID());
- if (SSL_LOG_CERTS) {
- Log::PrintErr("\titem %ld has a trust object\n", i);
- }
- // TODO(zra): Is this useful for anything?
- }
-
- // Identity.
- CFTypeRef identity_item =
- CFDictionaryGetValue(dict, kSecImportItemIdentity);
- if (identity_item != NULL) {
- ASSERT(CFGetTypeID(identity_item) == SecIdentityGetTypeID());
- if (SSL_LOG_CERTS) {
- Log::PrintErr("\titem %ld has an identity object\n", i);
- }
- // Only extract the first identity we find.
- if (result_identity == NULL) {
- result_identity =
- reinterpret_cast<SecIdentityRef>(const_cast<void*>(identity_item));
- CFRetain(result_identity);
- }
- }
-
- // Certificates.
- CFTypeRef cert_items = CFDictionaryGetValue(dict, kSecImportItemCertChain);
- if (cert_items != NULL) {
- ASSERT(CFGetTypeID(cert_items) == CFArrayGetTypeID());
- CFArrayRef certs = reinterpret_cast<CFArrayRef>(cert_items);
- if (SSL_LOG_CERTS) {
- CFIndex count = CFArrayGetCount(certs);
- Log::PrintErr("\titem %ld has a cert chain %ld certs long\n", i, count);
- }
- CFArrayAppendArray(result_certs, certs,
- CFRangeMake(0, CFArrayGetCount(certs)));
- }
- }
-
- if (out_certs == NULL) {
- if (result_certs != NULL) {
- CFRelease(result_certs);
- }
- } else {
- *out_certs = result_certs;
- }
-
- if (out_identity == NULL) {
- if (result_identity != NULL) {
- CFRelease(result_identity);
- }
- } else {
- *out_identity = result_identity;
- }
-
- // On failure, don't return any objects.
- ASSERT((status == noErr) ||
- ((result_certs == NULL) && (result_identity == NULL)));
- return status;
-}
-
-
-static OSStatus ExtractSecItems(uint8_t* buffer,
- intptr_t length,
- const char* password,
- CFArrayRef* out_certs,
- SecIdentityRef* out_identity) {
- ASSERT(buffer != NULL);
- ASSERT(password != NULL);
- OSStatus status = noErr;
-
- CFDataRef cfdata =
- CFDataCreateWithBytesNoCopy(NULL, buffer, length, kCFAllocatorNull);
- CFStringRef cfpassword = CFStringCreateWithCStringNoCopy(
- NULL, password, kCFStringEncodingUTF8, kCFAllocatorNull);
- ASSERT(cfdata != NULL);
- ASSERT(cfpassword != NULL);
-
- status = TryPKCS12Import(cfdata, cfpassword, out_certs, out_identity);
-
- CFRelease(cfdata);
- CFRelease(cfpassword);
- return status;
-}
-
-
-void FUNCTION_NAME(SecureSocket_Init)(Dart_NativeArguments args) {
- Dart_Handle dart_this = ThrowIfError(Dart_GetNativeArgument(args, 0));
- SSLFilter* filter = new SSLFilter(); // Deleted in DeleteFilter finalizer.
- 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 dart_this = ThrowIfError(Dart_GetNativeArgument(args, 0));
- 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));
-
- 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, kSecurityContextNativeFieldIndex,
- reinterpret_cast<intptr_t*>(&context)));
- }
-
- GetFilter(args)->Connect(dart_this, host_name, context, is_server,
- request_client_certificate,
- require_client_certificate);
-}
-
-
-void FUNCTION_NAME(SecureSocket_Destroy)(Dart_NativeArguments args) {
- SSLFilter* filter = GetFilter(args);
- // The SSLFilter is deleted in the finalizer for the Dart object created by
- // SetFilter. There is no need to NULL-out the native field for the SSLFilter
- // here because the SSLFilter won't be deleted until the finalizer for the
- // Dart object runs while the Dart object is being GCd. This approach avoids a
- // leak if Destroy isn't called, and avoids a NULL-dereference if Destroy is
- // called more than once.
- filter->Destroy();
-}
-
-
-void FUNCTION_NAME(SecureSocket_Handshake)(Dart_NativeArguments args) {
- OSStatus status = GetFilter(args)->CheckHandshake();
- CheckStatus(status, "HandshakeException", "Handshake error");
-}
-
-
-void FUNCTION_NAME(SecureSocket_GetSelectedProtocol)(
- Dart_NativeArguments args) {
- Dart_SetReturnValue(args, Dart_Null());
-}
-
-
-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_SetReturnValue(args, GetFilter(args)->PeerCertificate());
-}
-
-
-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));
-}
-
-
-void FUNCTION_NAME(SecurityContext_Allocate)(Dart_NativeArguments args) {
- SSLCertContext* cert_context = new SSLCertContext();
- // cert_context deleted in DeleteCertContext finalizer.
- Dart_Handle err = SetSecurityContext(args, cert_context);
- if (Dart_IsError(err)) {
- cert_context->Release();
- Dart_PropagateError(err);
- }
-}
-
-
-void FUNCTION_NAME(SecurityContext_UsePrivateKeyBytes)(
- Dart_NativeArguments args) {
- SSLCertContext* context = GetSecurityContext(args);
- const char* password = GetPasswordArgument(args, 2);
-
- OSStatus status;
- CFArrayRef cert_chain = NULL;
- SecIdentityRef identity = NULL;
- {
- ScopedMemBuffer buffer(ThrowIfError(Dart_GetNativeArgument(args, 1)));
- status = ExtractSecItems(buffer.get(), buffer.length(), password,
- &cert_chain, &identity);
- }
-
- // Set the context fields. Repeated calls to usePrivateKeyBytes are an error.
- bool set_failure = false;
- if ((identity != NULL) && !context->set_identity(identity)) {
- CFRelease(identity);
- if (cert_chain != NULL) {
- CFRelease(cert_chain);
- }
- set_failure = true;
- }
-
- // We can't have set a cert_chain without also having set an identity.
- // That is, if context->set_identity() succeeds, then it is impossible for
- // context->set_cert_chain() to fail. This is because SecPKCS12Import never
- // returns a cert chain without also returning a private key.
- ASSERT(set_failure || (context->cert_chain() == NULL));
- if (!set_failure && (cert_chain != NULL)) {
- context->set_cert_chain(cert_chain);
- }
-
- if (set_failure) {
- Dart_ThrowException(DartUtils::NewDartArgumentError(
- "usePrivateKeyBytes has already been called on the given context."));
- }
- CheckStatus(status, "TlsException", "Failure in usePrivateKeyBytes");
-}
-
-
-void FUNCTION_NAME(SecurityContext_SetTrustedCertificatesBytes)(
- Dart_NativeArguments args) {
- SSLCertContext* context = GetSecurityContext(args);
-
- OSStatus status = noErr;
- SecCertificateRef cert = NULL;
- {
- ScopedMemBuffer buffer(ThrowIfError(Dart_GetNativeArgument(args, 1)));
- CFDataRef cfdata = CFDataCreateWithBytesNoCopy(
- NULL, buffer.get(), buffer.length(), kCFAllocatorNull);
- cert = SecCertificateCreateWithData(NULL, cfdata);
- CFRelease(cfdata);
- }
-
- // Add the certs to the context.
- if (cert != NULL) {
- context->add_trusted_cert(cert);
- } else {
- status = errSSLBadCert;
- }
- CheckStatus(status, "TlsException", "Failure in setTrustedCertificatesBytes");
-}
-
-
-void FUNCTION_NAME(SecurityContext_AlpnSupported)(Dart_NativeArguments args) {
- Dart_SetReturnValue(args, Dart_NewBoolean(false));
-}
-
-
-void FUNCTION_NAME(SecurityContext_TrustBuiltinRoots)(
- Dart_NativeArguments args) {
- SSLCertContext* context = GetSecurityContext(args);
- context->set_trust_builtin(true);
-}
-
-
-void FUNCTION_NAME(SecurityContext_UseCertificateChainBytes)(
- Dart_NativeArguments args) {
- // This is a no-op on iOS. We get the cert chain along with the private key
- // in UsePrivateyKeyBytes().
-}
-
-
-void FUNCTION_NAME(SecurityContext_SetClientAuthoritiesBytes)(
- Dart_NativeArguments args) {
- Dart_ThrowException(DartUtils::NewDartUnsupportedError(
- "SecurityContext.setClientAuthoritiesBytes is not supported on this "
- "platform."));
-}
-
-
-void FUNCTION_NAME(SecurityContext_SetAlpnProtocols)(
- Dart_NativeArguments args) {
- Dart_ThrowException(DartUtils::NewDartUnsupportedError(
- "ALPN is not supported on this platform"));
-}
-
-
-void FUNCTION_NAME(X509_Subject)(Dart_NativeArguments args) {
- SecCertificateRef certificate = GetX509Certificate(args);
- CFStringRef cfsubject = SecCertificateCopySubjectSummary(certificate);
- if (cfsubject != NULL) {
- char* csubject = CFStringRefToCString(cfsubject);
- CFRelease(cfsubject);
- Dart_SetReturnValue(args, Dart_NewStringFromCString(csubject));
- } else {
- Dart_ThrowException(DartUtils::NewDartArgumentError(
- "X509.subject failed to find subject's common name."));
- }
-}
-
-
-void FUNCTION_NAME(X509_Issuer)(Dart_NativeArguments args) {
- Dart_ThrowException(DartUtils::NewDartUnsupportedError(
- "X509Certificate.issuer is not supported on this platform."));
-}
-
-
-void FUNCTION_NAME(X509_StartValidity)(Dart_NativeArguments args) {
- Dart_ThrowException(DartUtils::NewDartUnsupportedError(
- "X509Certificate.startValidity is not supported on this platform."));
-}
-
-
-void FUNCTION_NAME(X509_EndValidity)(Dart_NativeArguments args) {
- Dart_ThrowException(DartUtils::NewDartUnsupportedError(
- "X509Certificate.endValidity is not supported on this platform."));
-}
-
-
-// Pushes data through the SSL filter, reading and writing from circular
-// buffers shared with Dart. Called from the IOService thread.
-//
-// 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();
- intptr_t starts[SSLFilter::kNumBuffers];
- intptr_t ends[SSLFilter::kNumBuffers];
- for (intptr_t i = 0; i < SSLFilter::kNumBuffers; ++i) {
- starts[i] = CObjectInt32(request[2 * i + 2]).Value();
- ends[i] = CObjectInt32(request[2 * i + 3]).Value();
- }
-
- OSStatus status = filter->ProcessAllBuffers(starts, ends, in_handshake);
- if (status == noErr) {
- CObjectArray* result =
- new CObjectArray(CObject::NewArray(SSLFilter::kNumBuffers * 2));
- for (intptr_t 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 {
- TextBuffer status_message(SSL_ERROR_MESSAGE_BUFFER_SIZE);
- status_message.Printf("OSStatus = %ld: https://www.osstatus.com",
- static_cast<intptr_t>(status));
- CObjectArray* result = new CObjectArray(CObject::NewArray(2));
- result->SetAt(0, new CObjectInt32(CObject::NewInt32(status)));
- result->SetAt(1,
- new CObjectString(CObject::NewString(status_message.buf())));
- return result;
- }
-}
-
-
-// Usually buffer_starts_ and buffer_ends_ are populated by ProcessAllBuffers,
-// called from ProcessFilterRequest, called from the IOService thread.
-// However, the first call to SSLHandshake comes from the Dart thread, and so
-// doesn't go through there. This results in calls to SSLReadCallback and
-// SSLWriteCallback in which buffer_starts_ and buffer_ends_ haven't been set
-// up. In that case, since we're coming from Dart anyway, we can access the
-// fieds directly from the Dart objects.
-intptr_t SSLFilter::GetBufferStart(intptr_t idx) const {
- if (buffer_starts_[idx] != NULL) {
- return *buffer_starts_[idx];
- }
- Dart_Handle buffer_handle =
- ThrowIfError(Dart_HandleFromPersistent(dart_buffer_objects_[idx]));
- Dart_Handle start_handle =
- ThrowIfError(Dart_GetField(buffer_handle, DartUtils::NewString("start")));
- int64_t start = DartUtils::GetIntegerValue(start_handle);
- return static_cast<intptr_t>(start);
-}
-
-
-intptr_t SSLFilter::GetBufferEnd(intptr_t idx) const {
- if (buffer_ends_[idx] != NULL) {
- return *buffer_ends_[idx];
- }
- Dart_Handle buffer_handle =
- ThrowIfError(Dart_HandleFromPersistent(dart_buffer_objects_[idx]));
- Dart_Handle end_handle =
- ThrowIfError(Dart_GetField(buffer_handle, DartUtils::NewString("end")));
- int64_t end = DartUtils::GetIntegerValue(end_handle);
- return static_cast<intptr_t>(end);
-}
-
-
-void SSLFilter::SetBufferStart(intptr_t idx, intptr_t value) {
- if (buffer_starts_[idx] != NULL) {
- *buffer_starts_[idx] = value;
- return;
- }
- Dart_Handle buffer_handle =
- ThrowIfError(Dart_HandleFromPersistent(dart_buffer_objects_[idx]));
- ThrowIfError(DartUtils::SetIntegerField(buffer_handle, "start",
- static_cast<int64_t>(value)));
-}
-
-
-void SSLFilter::SetBufferEnd(intptr_t idx, intptr_t value) {
- if (buffer_ends_[idx] != NULL) {
- *buffer_ends_[idx] = value;
- return;
- }
- Dart_Handle buffer_handle =
- ThrowIfError(Dart_HandleFromPersistent(dart_buffer_objects_[idx]));
- ThrowIfError(DartUtils::SetIntegerField(buffer_handle, "end",
- static_cast<int64_t>(value)));
-}
-
-
-OSStatus SSLFilter::ProcessAllBuffers(intptr_t starts[kNumBuffers],
- intptr_t ends[kNumBuffers],
- bool in_handshake) {
- for (intptr_t i = 0; i < kNumBuffers; ++i) {
- buffer_starts_[i] = &starts[i];
- buffer_ends_[i] = &ends[i];
- }
-
- if (in_handshake) {
- OSStatus status = Handshake();
- if (status != noErr) {
- return status;
- }
- } else {
- for (intptr_t i = 0; i < kNumBuffers; ++i) {
- intptr_t start = starts[i];
- intptr_t end = ends[i];
- intptr_t 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:
- // 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).
- intptr_t buffer_end = (start == 0) ? size - 1 : size;
- intptr_t bytes = 0;
- OSStatus status =
- ProcessReadPlaintextBuffer(end, buffer_end, &bytes);
- if ((status != noErr) && (status != errSSLWouldBlock)) {
- return status;
- }
- end += bytes;
- ASSERT(end <= size);
- if (end == size) {
- end = 0;
- }
- }
- if (start > end + 1) {
- intptr_t bytes = 0;
- OSStatus status =
- ProcessReadPlaintextBuffer(end, start - 1, &bytes);
- if ((status != noErr) && (status != errSSLWouldBlock)) {
- return status;
- }
- end += bytes;
- ASSERT(end < start);
- }
- ends[i] = end;
- break;
- 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).
- intptr_t bytes = 0;
- OSStatus status = ProcessWritePlaintextBuffer(start, size, &bytes);
- if ((status != noErr) && (status != errSSLWouldBlock)) {
- return status;
- }
- start += bytes;
- ASSERT(start <= size);
- if (start == size) {
- start = 0;
- }
- }
- if (start < end) {
- intptr_t bytes = 0;
- OSStatus status = ProcessWritePlaintextBuffer(start, end, &bytes);
- if ((status != noErr) && (status != errSSLWouldBlock)) {
- return status;
- }
- start += bytes;
- ASSERT(start <= end);
- }
- starts[i] = start;
- break;
- case kReadEncrypted:
- case kWriteEncrypted:
- // These buffers are advanced through SSLReadCallback and
- // SSLWriteCallback, which are called from SSLRead, SSLWrite, and
- // SSLHandshake.
- break;
- default:
- UNREACHABLE();
- }
- }
- }
-
- for (intptr_t i = 0; i < kNumBuffers; ++i) {
- buffer_starts_[i] = NULL;
- buffer_ends_[i] = NULL;
- }
- return noErr;
-}
-
-
-Dart_Handle SSLFilter::Init(Dart_Handle dart_this) {
- 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<intptr_t>(buffer_size);
- encrypted_buffer_size_ = static_cast<intptr_t>(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);
- buffer_starts_[i] = NULL;
- buffer_ends_[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() {
- if (peer_certs_ == NULL) {
- return Dart_Null();
- }
-
- CFTypeRef item = CFArrayGetValueAtIndex(peer_certs_, 0);
- ASSERT(CFGetTypeID(item) == SecCertificateGetTypeID());
- SecCertificateRef cert =
- reinterpret_cast<SecCertificateRef>(const_cast<void*>(item));
- if (cert == NULL) {
- return Dart_Null();
- }
-
- return WrappedX509Certificate(cert);
-}
-
-
-void SSLFilter::Connect(Dart_Handle dart_this,
- const char* hostname,
- SSLCertContext* context,
- bool is_server,
- bool request_client_certificate,
- bool require_client_certificate) {
- if (in_handshake_) {
- FATAL("Connect called twice on the same _SecureFilter.");
- }
-
- // Create the underlying context
- SSLContextRef ssl_context = SSLCreateContext(
- NULL, is_server ? kSSLServerSide : kSSLClientSide, kSSLStreamType);
-
- // Configure the context.
- OSStatus status;
- status = SSLSetPeerDomainName(ssl_context, hostname, strlen(hostname));
- CheckStatus(status, "TlsException", "Failed to set peer domain name");
-
- status = SSLSetIOFuncs(ssl_context, SSLFilter::SSLReadCallback,
- SSLFilter::SSLWriteCallback);
- CheckStatus(status, "TlsException", "Failed to set IO Callbacks");
-
- status =
- SSLSetConnection(ssl_context, reinterpret_cast<SSLConnectionRef>(this));
- CheckStatus(status, "TlsException", "Failed to set connection object");
-
- // Always evaluate the certs manually so that we can cache the peer
- // certificates in the context for calls to peerCertificate.
- status = SSLSetSessionOption(ssl_context, kSSLSessionOptionBreakOnServerAuth,
- true);
- CheckStatus(status, "TlsException", "Failed to set BreakOnServerAuth option");
-
- status = SSLSetProtocolVersionMin(ssl_context, kTLSProtocol1);
- CheckStatus(status, "TlsException",
- "Failed to set minimum protocol version to kTLSProtocol1");
-
- // If the context has an identity pass it to SSLSetCertificate().
- if (context->identity() != NULL) {
- CFMutableArrayRef chain =
- CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
- CFArrayAppendValue(chain, context->identity());
-
- // Append the certificate chain if there is one.
- if (context->cert_chain() != NULL) {
- // Skip the first one, it's already included in the identity.
- CFIndex chain_length = CFArrayGetCount(context->cert_chain());
- if (chain_length > 1) {
- CFArrayAppendArray(chain, context->cert_chain(),
- CFRangeMake(1, chain_length));
- }
- }
-
- status = SSLSetCertificate(ssl_context, chain);
- CFRelease(chain);
- CheckStatus(status, "TlsException", "SSLSetCertificate failed");
- }
-
- if (is_server) {
- SSLAuthenticate auth =
- require_client_certificate
- ? kAlwaysAuthenticate
- : (request_client_certificate ? kTryAuthenticate
- : kNeverAuthenticate);
- status = SSLSetClientSideAuthenticate(ssl_context, auth);
- CheckStatus(status, "TlsException",
- "Failed to set client authentication mode");
-
- // If we're at least trying client authentication, then break handshake
- // for client authentication.
- if (auth != kNeverAuthenticate) {
- status = SSLSetSessionOption(ssl_context,
- kSSLSessionOptionBreakOnClientAuth, true);
- CheckStatus(status, "TlsException",
- "Failed to set client authentication mode");
- }
- }
-
- // Add the contexts to our wrapper.
- cert_context_.set(context);
- ssl_context_ = ssl_context;
- is_server_ = is_server;
-
- // Kick-off the handshake. Expect the handshake to need more data.
- // SSLHandshake calls our SSLReadCallback and SSLWriteCallback.
- status = SSLHandshake(ssl_context);
- ASSERT(status != noErr);
- if (status == errSSLWouldBlock) {
- status = noErr;
- in_handshake_ = true;
- }
- CheckStatus(status, "HandshakeException", is_server_
- ? "Handshake error in server"
- : "Handshake error in client");
-}
-
-
-OSStatus SSLFilter::EvaluatePeerTrust() {
- OSStatus status = noErr;
-
- if (SSL_LOG_STATUS) {
- Log::PrintErr("Handshake evaluating trust.\n");
- }
- SecTrustRef peer_trust = NULL;
- status = SSLCopyPeerTrust(ssl_context_, &peer_trust);
- if (status != noErr) {
- if (is_server_ && (status == errSSLBadCert)) {
- // A client certificate was requested, but not required, and wasn't sent.
- return noErr;
- }
- if (SSL_LOG_STATUS) {
- Log::PrintErr("Handshake error from SSLCopyPeerTrust(): %ld.\n",
- static_cast<intptr_t>(status));
- }
- return status;
- }
-
- CFArrayRef trusted_certs = NULL;
- if (cert_context_.get()->trusted_certs() != NULL) {
- trusted_certs =
- CFArrayCreateCopy(NULL, cert_context_.get()->trusted_certs());
- } else {
- trusted_certs = CFArrayCreate(NULL, NULL, 0, &kCFTypeArrayCallBacks);
- }
-
- status = SecTrustSetAnchorCertificates(peer_trust, trusted_certs);
- if (status != noErr) {
- if (SSL_LOG_STATUS) {
- Log::PrintErr("Handshake error from SecTrustSetAnchorCertificates: %ld\n",
- static_cast<intptr_t>(status));
- }
- CFRelease(trusted_certs);
- CFRelease(peer_trust);
- return status;
- }
-
- if (SSL_LOG_STATUS) {
- Log::PrintErr(
- "Handshake %s built in root certs\n",
- cert_context_.get()->trust_builtin() ? "trusting" : "not trusting");
- }
-
- status = SecTrustSetAnchorCertificatesOnly(
- peer_trust, !cert_context_.get()->trust_builtin());
- if (status != noErr) {
- CFRelease(trusted_certs);
- CFRelease(peer_trust);
- return status;
- }
-
- SecTrustResultType trust_result;
- status = SecTrustEvaluate(peer_trust, &trust_result);
- if (status != noErr) {
- CFRelease(trusted_certs);
- CFRelease(peer_trust);
- return status;
- }
-
- // Grab the peer's certificate chain.
- CFIndex peer_chain_length = SecTrustGetCertificateCount(peer_trust);
- CFMutableArrayRef peer_certs =
- CFArrayCreateMutable(NULL, peer_chain_length, &kCFTypeArrayCallBacks);
- for (CFIndex i = 0; i < peer_chain_length; ++i) {
- CFArrayAppendValue(peer_certs,
- SecTrustGetCertificateAtIndex(peer_trust, i));
- }
- peer_certs_ = peer_certs;
-
- CFRelease(trusted_certs);
- CFRelease(peer_trust);
-
- if ((trust_result == kSecTrustResultProceed) ||
- (trust_result == kSecTrustResultUnspecified)) {
- // Trusted.
- return noErr;
- } else {
- if (SSL_LOG_STATUS) {
- Log::PrintErr("Trust eval failed: trust_result = %d\n", trust_result);
- }
- bad_cert_ = true;
- return errSSLBadCert;
- }
-}
-
-
-OSStatus SSLFilter::Handshake() {
- ASSERT(cert_context_.get() != NULL);
- ASSERT(ssl_context_ != NULL);
- // Try and push handshake along.
- if (SSL_LOG_STATUS) {
- Log::PrintErr("Doing SSLHandshake\n");
- }
- OSStatus status = SSLHandshake(ssl_context_);
- if (SSL_LOG_STATUS) {
- Log::PrintErr("SSLHandshake returned %ld\n", static_cast<intptr_t>(status));
- }
-
- if ((status == errSSLServerAuthCompleted) ||
- (status == errSSLClientAuthCompleted)) {
- status = EvaluatePeerTrust();
- if (status == errSSLBadCert) {
- // Need to invoke the bad certificate callback.
- return noErr;
- } else if (status != noErr) {
- return status;
- }
- // When trust evaluation succeeds, we can call SSLHandshake again
- // immediately.
- status = SSLHandshake(ssl_context_);
- }
-
- if (status == errSSLWouldBlock) {
- in_handshake_ = true;
- return noErr;
- }
-
- // Handshake succeeded.
- if ((in_handshake_) && (status == noErr)) {
- if (SSL_LOG_STATUS) {
- Log::PrintErr("Finished with the Handshake\n");
- }
- connected_ = true;
- }
- return status;
-}
-
-
-// Returns false if Handshake should fail, and true if Handshake should
-// proceed.
-Dart_Handle SSLFilter::InvokeBadCertCallback(SecCertificateRef peer_cert) {
- Dart_Handle callback = bad_certificate_callback_;
- if (Dart_IsNull(callback)) {
- return callback;
- }
- Dart_Handle args[1];
- args[0] = WrappedX509Certificate(peer_cert);
- if (Dart_IsError(args[0])) {
- return args[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()));
- }
- return result;
-}
-
-
-OSStatus SSLFilter::CheckHandshake() {
- if (bad_cert_ && in_handshake_) {
- if (SSL_LOG_STATUS) {
- Log::PrintErr("Invoking bad certificate callback\n");
- }
- ASSERT(peer_certs_ != NULL);
- CFIndex peer_certs_len = CFArrayGetCount(peer_certs_);
- ASSERT(peer_certs_len > 0);
- CFTypeRef item = CFArrayGetValueAtIndex(peer_certs_, peer_certs_len - 1);
- ASSERT(item != NULL);
- ASSERT(CFGetTypeID(item) == SecCertificateGetTypeID());
- SecCertificateRef peer_cert =
- reinterpret_cast<SecCertificateRef>(const_cast<void*>(item));
- Dart_Handle result = InvokeBadCertCallback(peer_cert);
- ThrowIfError(result);
- if (Dart_IsNull(result)) {
- return errSSLBadCert;
- } else {
- bool good_cert = DartUtils::GetBooleanValue(result);
- bad_cert_ = !good_cert;
- return good_cert ? noErr : errSSLBadCert;
- }
- }
-
- if (connected_ && in_handshake_) {
- if (SSL_LOG_STATUS) {
- Log::PrintErr("Invoking handshake complete callback\n");
- }
- ThrowIfError(Dart_InvokeClosure(
- Dart_HandleFromPersistent(handshake_complete_), 0, NULL));
- in_handshake_ = false;
- }
- return noErr;
-}
-
-
-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.
-}
-
-
-SSLFilter::~SSLFilter() {
- if (ssl_context_ != NULL) {
- CFRelease(ssl_context_);
- ssl_context_ = NULL;
- }
- if (peer_certs_ != NULL) {
- CFRelease(peer_certs_);
- peer_certs_ = 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;
- }
- }
-}
-
-
-void SSLFilter::Destroy() {
- if (ssl_context_ != NULL) {
- SSLClose(ssl_context_);
- }
- 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;
- }
-}
-
-
-OSStatus SSLFilter::SSLReadCallback(SSLConnectionRef connection,
- void* data,
- size_t* data_requested) {
- // Copy at most `data_requested` bytes from `buffers_[kReadEncrypted]` into
- // `data`
- ASSERT(connection != NULL);
- ASSERT(data != NULL);
- ASSERT(data_requested != NULL);
-
- SSLFilter* filter =
- const_cast<SSLFilter*>(reinterpret_cast<const SSLFilter*>(connection));
- uint8_t* datap = reinterpret_cast<uint8_t*>(data);
- uint8_t* buffer = filter->buffers_[kReadEncrypted];
- intptr_t start = filter->GetBufferStart(kReadEncrypted);
- intptr_t end = filter->GetBufferEnd(kReadEncrypted);
- intptr_t size = filter->encrypted_buffer_size_;
- intptr_t requested = static_cast<intptr_t>(*data_requested);
- intptr_t data_read = 0;
-
- if (end < start) {
- // Data may be split into two segments. In this case,
- // the first is [start, size).
- intptr_t buffer_end = (start == 0) ? size - 1 : size;
- intptr_t available = buffer_end - start;
- intptr_t bytes = requested < available ? requested : available;
- memmove(datap, &buffer[start], bytes);
- start += bytes;
- datap += bytes;
- data_read += bytes;
- requested -= bytes;
- ASSERT(start <= size);
- if (start == size) {
- start = 0;
- }
- }
- if ((requested > 0) && (start < end)) {
- intptr_t available = end - start;
- intptr_t bytes = requested < available ? requested : available;
- memmove(datap, &buffer[start], bytes);
- start += bytes;
- datap += bytes;
- data_read += bytes;
- requested -= bytes;
- ASSERT(start <= end);
- }
-
- if (SSL_LOG_DATA) {
- Log::PrintErr("SSLReadCallback: requested: %ld, read %ld bytes\n",
- *data_requested, data_read);
- }
-
- filter->SetBufferStart(kReadEncrypted, start);
- bool short_read = data_read < static_cast<intptr_t>(*data_requested);
- *data_requested = data_read;
- return short_read ? errSSLWouldBlock : noErr;
-}
-
-
-// Read decrypted data from the filter to the circular buffer.
-OSStatus SSLFilter::ProcessReadPlaintextBuffer(intptr_t start,
- intptr_t end,
- intptr_t* bytes_processed) {
- ASSERT(bytes_processed != NULL);
- intptr_t length = end - start;
- OSStatus status = noErr;
- size_t bytes = 0;
- if (length > 0) {
- status =
- SSLRead(ssl_context_,
- reinterpret_cast<void*>((buffers_[kReadPlaintext] + start)),
- length, &bytes);
- if (SSL_LOG_STATUS) {
- Log::PrintErr("SSLRead: status = %ld\n", static_cast<intptr_t>(status));
- }
- if ((status != noErr) && (status != errSSLWouldBlock)) {
- *bytes_processed = 0;
- return status;
- }
- }
- if (SSL_LOG_DATA) {
- Log::PrintErr(
- "ProcessReadPlaintextBuffer: requested: %ld, read %ld bytes\n", length,
- bytes);
- }
- *bytes_processed = static_cast<intptr_t>(bytes);
- return status;
-}
-
-
-OSStatus SSLFilter::SSLWriteCallback(SSLConnectionRef connection,
- const void* data,
- size_t* data_provided) {
- // Copy at most `data_provided` bytes from data into
- // `buffers_[kWriteEncrypted]`.
- ASSERT(connection != NULL);
- ASSERT(data != NULL);
- ASSERT(data_provided != NULL);
-
- SSLFilter* filter =
- const_cast<SSLFilter*>(reinterpret_cast<const SSLFilter*>(connection));
- const uint8_t* datap = reinterpret_cast<const uint8_t*>(data);
- uint8_t* buffer = filter->buffers_[kWriteEncrypted];
- intptr_t start = filter->GetBufferStart(kWriteEncrypted);
- intptr_t end = filter->GetBufferEnd(kWriteEncrypted);
- intptr_t size = filter->encrypted_buffer_size_;
- intptr_t provided = static_cast<intptr_t>(*data_provided);
- intptr_t data_written = 0;
-
- // 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).
- intptr_t buffer_end = (start == 0) ? size - 1 : size;
- intptr_t available = buffer_end - end;
- intptr_t bytes = provided < available ? provided : available;
- memmove(&buffer[end], datap, bytes);
- end += bytes;
- datap += bytes;
- data_written += bytes;
- provided -= bytes;
- ASSERT(end <= size);
- if (end == size) {
- end = 0;
- }
- }
- if ((provided > 0) && (start > end + 1)) {
- intptr_t available = (start - 1) - end;
- intptr_t bytes = provided < available ? provided : available;
- memmove(&buffer[end], datap, bytes);
- end += bytes;
- datap += bytes;
- data_written += bytes;
- provided -= bytes;
- ASSERT(end < start);
- }
-
- if (SSL_LOG_DATA) {
- Log::PrintErr("SSLWriteCallback: provided: %ld, written %ld bytes\n",
- *data_provided, data_written);
- }
-
- filter->SetBufferEnd(kWriteEncrypted, end);
- *data_provided = data_written;
- return (data_written == 0) ? errSSLWouldBlock : noErr;
-}
-
-
-OSStatus SSLFilter::ProcessWritePlaintextBuffer(intptr_t start,
- intptr_t end,
- intptr_t* bytes_processed) {
- ASSERT(bytes_processed != NULL);
- intptr_t length = end - start;
- OSStatus status = noErr;
- size_t bytes = 0;
- if (length > 0) {
- status =
- SSLWrite(ssl_context_,
- reinterpret_cast<void*>(buffers_[kWritePlaintext] + start),
- length, &bytes);
- if (SSL_LOG_STATUS) {
- Log::PrintErr("SSLWrite: status = %ld\n", static_cast<intptr_t>(status));
- }
- if ((status != noErr) && (status != errSSLWouldBlock)) {
- *bytes_processed = 0;
- return status;
- }
- }
- if (SSL_LOG_DATA) {
- Log::PrintErr("ProcessWritePlaintextBuffer: requested: %ld, written: %ld\n",
- length, bytes);
- }
- *bytes_processed = static_cast<intptr_t>(bytes);
- return status;
-}
-
-} // namespace bin
-} // namespace dart
-
-#endif // HOST_OS_IOS
-
-#endif // !defined(DART_IO_SECURE_SOCKET_DISABLED)
diff --git a/runtime/bin/secure_socket_ios.h b/runtime/bin/secure_socket_ios.h
deleted file mode 100644
index 8a59cb7..0000000
--- a/runtime/bin/secure_socket_ios.h
+++ /dev/null
@@ -1,239 +0,0 @@
-// Copyright (c) 2016, 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.
-
-#ifndef RUNTIME_BIN_SECURE_SOCKET_IOS_H_
-#define RUNTIME_BIN_SECURE_SOCKET_IOS_H_
-
-#if !defined(RUNTIME_BIN_SECURE_SOCKET_H_)
-#error Do not include secure_socket_macos.h directly. Use secure_socket.h.
-#endif
-
-#include <stdlib.h>
-#include <string.h>
-#include <stdio.h>
-#include <sys/types.h>
-
-#include <CoreFoundation/CoreFoundation.h>
-#include <Security/SecureTransport.h>
-#include <Security/Security.h>
-
-#include "bin/builtin.h"
-#include "bin/dartutils.h"
-#include "bin/lockers.h"
-#include "bin/reference_counting.h"
-#include "bin/socket.h"
-#include "bin/thread.h"
-#include "bin/utils.h"
-
-namespace dart {
-namespace bin {
-
-// SSLCertContext wraps the certificates needed for a SecureTransport
-// connection. Fields are protected by the mutex_ field, and may only be set
-// once. This is to allow access by both the Dart thread and the IOService
-// thread. Setters return false if the field was already set.
-class SSLCertContext : public ReferenceCounted<SSLCertContext> {
- public:
- SSLCertContext()
- : ReferenceCounted(),
- mutex_(new Mutex()),
- trusted_certs_(NULL),
- identity_(NULL),
- cert_chain_(NULL),
- trust_builtin_(false) {}
-
- ~SSLCertContext() {
- {
- MutexLocker m(mutex_);
- if (trusted_certs_ != NULL) {
- CFRelease(trusted_certs_);
- }
- if (identity_ != NULL) {
- CFRelease(identity_);
- }
- if (cert_chain_ != NULL) {
- CFRelease(cert_chain_);
- }
- }
- delete mutex_;
- }
-
- CFMutableArrayRef trusted_certs() {
- MutexLocker m(mutex_);
- return trusted_certs_;
- }
- void add_trusted_cert(SecCertificateRef trusted_cert) {
- // Takes ownership of trusted_cert.
- MutexLocker m(mutex_);
- if (trusted_certs_ == NULL) {
- trusted_certs_ = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
- }
- CFArrayAppendValue(trusted_certs_, trusted_cert);
- CFRelease(trusted_cert); // trusted_cert is retained by the array.
- }
-
- SecIdentityRef identity() {
- MutexLocker m(mutex_);
- return identity_;
- }
- bool set_identity(SecIdentityRef identity) {
- MutexLocker m(mutex_);
- if (identity_ == NULL) {
- identity_ = identity;
- return true;
- }
- return false;
- }
-
- CFArrayRef cert_chain() {
- MutexLocker m(mutex_);
- return cert_chain_;
- }
- bool set_cert_chain(CFArrayRef cert_chain) {
- MutexLocker m(mutex_);
- if (cert_chain_ == NULL) {
- cert_chain_ = cert_chain;
- return true;
- }
- return false;
- }
-
- bool trust_builtin() {
- MutexLocker m(mutex_);
- return trust_builtin_;
- }
- void set_trust_builtin(bool trust_builtin) {
- MutexLocker m(mutex_);
- trust_builtin_ = trust_builtin;
- }
-
- private:
- // The context is accessed both by Dart code and the IOService. This mutex
- // protects all fields.
- Mutex* mutex_;
- CFMutableArrayRef trusted_certs_;
- SecIdentityRef identity_;
- CFArrayRef cert_chain_;
- bool trust_builtin_;
-
- DISALLOW_COPY_AND_ASSIGN(SSLCertContext);
-};
-
-// SSLFilter encapsulates the SecureTransport code in a filter that communicates
-// with the containing _SecureFilterImpl Dart object through four shared
-// ExternalByteArray buffers, for reading and writing plaintext, and
-// reading and writing encrypted text. The filter handles handshaking
-// and certificate verification.
-class SSLFilter : public ReferenceCounted<SSLFilter> {
- public:
- // These enums must agree with those in sdk/lib/io/secure_socket.dart.
- enum BufferIndex {
- kReadPlaintext,
- kWritePlaintext,
- kReadEncrypted,
- kWriteEncrypted,
- kNumBuffers,
- kFirstEncrypted = kReadEncrypted
- };
-
- SSLFilter()
- : ReferenceCounted(),
- cert_context_(NULL),
- ssl_context_(NULL),
- peer_certs_(NULL),
- string_start_(NULL),
- string_length_(NULL),
- handshake_complete_(NULL),
- bad_certificate_callback_(NULL),
- in_handshake_(false),
- connected_(false),
- bad_cert_(false),
- is_server_(false),
- hostname_(NULL) {}
-
- ~SSLFilter();
-
- // Callback called by the IOService.
- static CObject* ProcessFilterRequest(const CObjectArray& request);
-
- Dart_Handle Init(Dart_Handle dart_this);
- void Connect(Dart_Handle dart_this,
- const char* hostname,
- SSLCertContext* context,
- bool is_server,
- bool request_client_certificate,
- bool require_client_certificate);
- void Destroy();
- OSStatus CheckHandshake();
- void Renegotiate(bool use_session_cache,
- bool request_client_certificate,
- bool require_client_certificate);
- void RegisterHandshakeCompleteCallback(Dart_Handle handshake_complete);
- void RegisterBadCertificateCallback(Dart_Handle callback);
- Dart_Handle PeerCertificate();
-
- private:
- static OSStatus SSLReadCallback(SSLConnectionRef connection,
- void* data,
- size_t* data_length);
- static OSStatus SSLWriteCallback(SSLConnectionRef connection,
- const void* data,
- size_t* data_length);
-
- static bool isBufferEncrypted(intptr_t i) {
- return static_cast<BufferIndex>(i) >= kFirstEncrypted;
- }
- Dart_Handle InitializeBuffers(Dart_Handle dart_this);
-
- intptr_t GetBufferStart(intptr_t idx) const;
- intptr_t GetBufferEnd(intptr_t idx) const;
- void SetBufferStart(intptr_t idx, intptr_t value);
- void SetBufferEnd(intptr_t idx, intptr_t value);
-
- OSStatus ProcessAllBuffers(intptr_t starts[kNumBuffers],
- intptr_t ends[kNumBuffers],
- bool in_handshake);
- OSStatus ProcessReadPlaintextBuffer(intptr_t start,
- intptr_t end,
- intptr_t* bytes_processed);
- OSStatus ProcessWritePlaintextBuffer(intptr_t start,
- intptr_t end,
- intptr_t* bytes_processed);
-
- // These calls can block on IO, and should only be invoked from
- // from ProcessAllBuffers from ProcessFilterRequest.
- OSStatus EvaluatePeerTrust();
- OSStatus Handshake();
- Dart_Handle InvokeBadCertCallback(SecCertificateRef peer_cert);
-
- RetainedPointer<SSLCertContext> cert_context_;
- SSLContextRef ssl_context_;
- CFArrayRef peer_certs_;
-
- // starts and ends filled in at the start of ProcessAllBuffers.
- // If these are NULL, then try to get the pointers out of
- // dart_buffer_objects_.
- uint8_t* buffers_[kNumBuffers];
- intptr_t* buffer_starts_[kNumBuffers];
- intptr_t* buffer_ends_[kNumBuffers];
- intptr_t buffer_size_;
- intptr_t encrypted_buffer_size_;
- Dart_PersistentHandle string_start_;
- Dart_PersistentHandle string_length_;
- Dart_PersistentHandle dart_buffer_objects_[kNumBuffers];
- Dart_PersistentHandle handshake_complete_;
- Dart_PersistentHandle bad_certificate_callback_;
- bool in_handshake_;
- bool connected_;
- bool bad_cert_;
- bool is_server_;
- char* hostname_;
-
- DISALLOW_COPY_AND_ASSIGN(SSLFilter);
-};
-
-} // namespace bin
-} // namespace dart
-
-#endif // RUNTIME_BIN_SECURE_SOCKET_IOS_H_
diff --git a/runtime/bin/secure_socket_macos.cc b/runtime/bin/secure_socket_macos.cc
deleted file mode 100644
index 520f2c9..0000000
--- a/runtime/bin/secure_socket_macos.cc
+++ /dev/null
@@ -1,1815 +0,0 @@
-// Copyright (c) 2016, 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 "platform/globals.h"
-#if defined(HOST_OS_MACOS) && !HOST_OS_IOS
-
-#include "bin/secure_socket.h"
-#include "bin/secure_socket_macos.h"
-
-#include <errno.h>
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <sys/syslimits.h>
-#include <stdio.h>
-#include <string.h>
-
-#include <CoreFoundation/CoreFoundation.h>
-#include <Security/SecureTransport.h>
-#include <Security/Security.h>
-
-#include "bin/builtin.h"
-#include "bin/dartutils.h"
-#include "bin/lockers.h"
-#include "bin/log.h"
-#include "bin/socket.h"
-#include "bin/thread.h"
-#include "bin/utils.h"
-
-#include "platform/text_buffer.h"
-#include "platform/utils.h"
-
-#include "include/dart_api.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; \
- } \
- }
-
-// We need to access this private API function to create a SecIdentityRef
-// without writing a custom keychain to the filesystem. This is the approach
-// taken in WebKit:
-// https://webkit.googlesource.com/WebKit/+/master/Source/WebKit2/Shared/cf/ArgumentCodersCF.cpp
-extern "C" {
-SecIdentityRef SecIdentityCreate(CFAllocatorRef allocator,
- SecCertificateRef certificate,
- SecKeyRef private_key);
-}
-
-namespace dart {
-namespace bin {
-
-static const int kSSLFilterNativeFieldIndex = 0;
-static const int kSecurityContextNativeFieldIndex = 0;
-static const int kX509NativeFieldIndex = 0;
-
-static const bool SSL_LOG_STATUS = false;
-static const bool SSL_LOG_DATA = false;
-static const bool SSL_LOG_CERTS = false;
-static const int SSL_ERROR_MESSAGE_BUFFER_SIZE = 1000;
-static const intptr_t PEM_BUFSIZE = 1024;
-
-static char* CFStringRefToCString(CFStringRef cfstring) {
- CFIndex len = CFStringGetLength(cfstring);
- CFIndex max_len =
- CFStringGetMaximumSizeForEncoding(len, kCFStringEncodingUTF8) + 1;
- char* result = reinterpret_cast<char*>(Dart_ScopeAllocate(max_len));
- ASSERT(result != NULL);
- bool success =
- CFStringGetCString(cfstring, result, max_len, kCFStringEncodingUTF8);
- return success ? result : NULL;
-}
-
-
-// Handle an error reported from the SecureTransport library.
-static void ThrowIOException(OSStatus status,
- const char* exception_type,
- const char* message) {
- TextBuffer status_message(SSL_ERROR_MESSAGE_BUFFER_SIZE);
- CFStringRef error_string = SecCopyErrorMessageString(status, NULL);
- if (error_string == NULL) {
- status_message.Printf("OSStatus = %ld: https://www.osstatus.com",
- static_cast<intptr_t>(status));
- } else {
- char* error = CFStringRefToCString(error_string);
- status_message.Printf("OSStatus = %ld: %s", static_cast<intptr_t>(status),
- error);
- CFRelease(error_string);
- }
- OSError os_error_struct(status, status_message.buf(), OSError::kBoringSSL);
- Dart_Handle os_error = DartUtils::NewDartOSError(&os_error_struct);
- Dart_Handle exception =
- DartUtils::NewDartIOException(exception_type, message, os_error);
- ASSERT(!Dart_IsError(exception));
- Dart_ThrowException(exception);
- UNREACHABLE();
-}
-
-
-static void CheckStatus(OSStatus status,
- const char* type,
- const char* message) {
- if (status == noErr) {
- return;
- }
- ThrowIOException(status, type, message);
-}
-
-
-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, 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);
- const int approximate_size_of_filter = 1500;
- 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, kSSLFilterNativeFieldIndex,
- reinterpret_cast<intptr_t>(filter));
- RETURN_IF_ERROR(err);
- Dart_NewWeakPersistentHandle(dart_this, reinterpret_cast<void*>(filter),
- approximate_size_of_filter, DeleteFilter);
- return Dart_Null();
-}
-
-
-static SSLCertContext* GetSecurityContext(Dart_NativeArguments args) {
- SSLCertContext* context;
- Dart_Handle dart_this = ThrowIfError(Dart_GetNativeArgument(args, 0));
- ASSERT(Dart_IsInstance(dart_this));
- ThrowIfError(
- Dart_GetNativeInstanceField(dart_this, kSecurityContextNativeFieldIndex,
- reinterpret_cast<intptr_t*>(&context)));
- return context;
-}
-
-
-static void DeleteCertContext(void* isolate_data,
- Dart_WeakPersistentHandle handle,
- void* context_pointer) {
- SSLCertContext* context = static_cast<SSLCertContext*>(context_pointer);
- context->Release();
-}
-
-
-static Dart_Handle SetSecurityContext(Dart_NativeArguments args,
- SSLCertContext* context) {
- const int approximate_size_of_context = 1500;
- 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, kSecurityContextNativeFieldIndex,
- reinterpret_cast<intptr_t>(context));
- RETURN_IF_ERROR(err);
- Dart_NewWeakPersistentHandle(dart_this, context, approximate_size_of_context,
- DeleteCertContext);
- return Dart_Null();
-}
-
-
-static SecCertificateRef GetX509Certificate(Dart_NativeArguments args) {
- SecCertificateRef certificate;
- Dart_Handle dart_this = ThrowIfError(Dart_GetNativeArgument(args, 0));
- ASSERT(Dart_IsInstance(dart_this));
- ThrowIfError(
- Dart_GetNativeInstanceField(dart_this, kX509NativeFieldIndex,
- reinterpret_cast<intptr_t*>(&certificate)));
- return certificate;
-}
-
-
-static void ReleaseCertificate(void* isolate_data,
- Dart_WeakPersistentHandle handle,
- void* context_pointer) {
- SecCertificateRef cert = reinterpret_cast<SecCertificateRef>(context_pointer);
- CFRelease(cert);
-}
-
-
-static Dart_Handle WrappedX509Certificate(SecCertificateRef certificate) {
- const intptr_t approximate_size_of_certificate = 1500;
- if (certificate == NULL) {
- return Dart_Null();
- }
- Dart_Handle x509_type =
- DartUtils::GetDartType(DartUtils::kIOLibURL, "X509Certificate");
- if (Dart_IsError(x509_type)) {
- return x509_type;
- }
- Dart_Handle arguments[] = {NULL};
-
- Dart_Handle result =
- Dart_New(x509_type, DartUtils::NewString("_"), 0, arguments);
- if (Dart_IsError(result)) {
- return result;
- }
- ASSERT(Dart_IsInstance(result));
-
- // CFRetain in case the returned Dart object outlives the SecurityContext.
- // CFRelease is in the Dart object's finalizer
- CFRetain(certificate);
- Dart_NewWeakPersistentHandle(result, reinterpret_cast<void*>(certificate),
- approximate_size_of_certificate,
- ReleaseCertificate);
-
- Dart_Handle status = Dart_SetNativeInstanceField(
- result, kX509NativeFieldIndex, reinterpret_cast<intptr_t>(certificate));
- if (Dart_IsError(status)) {
- return status;
- }
- return result;
-}
-
-
-static const char* GetPasswordArgument(Dart_NativeArguments args,
- intptr_t index) {
- Dart_Handle password_object =
- ThrowIfError(Dart_GetNativeArgument(args, index));
- const char* password = NULL;
- if (Dart_IsString(password_object)) {
- ThrowIfError(Dart_StringToCString(password_object, &password));
- if (strlen(password) > PEM_BUFSIZE - 1) {
- Dart_ThrowException(DartUtils::NewDartArgumentError(
- "Password length is greater than 1023 bytes."));
- }
- } else if (Dart_IsNull(password_object)) {
- password = "";
- } else {
- Dart_ThrowException(
- DartUtils::NewDartArgumentError("Password is not a String or null"));
- }
- return password;
-}
-
-
-static OSStatus GetKeyAndCerts(CFArrayRef items,
- CFIndex items_length,
- CFArrayRef* out_certs,
- SecKeyRef* out_key) {
- OSStatus status = noErr;
-
- // Loop through the items, take only the first private key/identity, ignore
- // any others, populate out_certs.
- CFMutableArrayRef certs =
- CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
- SecKeyRef key = NULL;
-
- for (CFIndex i = 0; i < items_length; ++i) {
- CFTypeRef item =
- reinterpret_cast<CFTypeRef>(CFArrayGetValueAtIndex(items, i));
- CFTypeID item_type = CFGetTypeID(item);
- if (item_type == SecCertificateGetTypeID()) {
- if (SSL_LOG_CERTS) {
- Log::Print("\titem %ld: Certificate\n", i);
- }
- CFArrayAppendValue(certs, item);
- } else if ((item_type == SecKeyGetTypeID()) && (key == NULL)) {
- if (SSL_LOG_CERTS) {
- Log::Print("\titem %ld: Key\n", i);
- }
- key = reinterpret_cast<SecKeyRef>(const_cast<void*>(item));
- CFRetain(key);
- } else if ((item_type == SecIdentityGetTypeID()) && (key == NULL)) {
- if (SSL_LOG_CERTS) {
- Log::Print("\titem %ld: Identity\n", i);
- }
- SecIdentityRef identity =
- reinterpret_cast<SecIdentityRef>(const_cast<void*>(item));
- SecCertificateRef cert = NULL;
-
- status = SecIdentityCopyPrivateKey(identity, &key);
- if (status != noErr) {
- CFRelease(certs);
- return status;
- }
-
- status = SecIdentityCopyCertificate(identity, &cert);
- if (status != noErr) {
- CFRelease(key);
- CFRelease(certs);
- return status;
- }
- CFArrayAppendValue(certs, cert);
- CFRelease(cert);
- }
- // Other item types are ignored.
- }
-
- if (out_key == NULL) {
- if (key != NULL) {
- CFRelease(key);
- }
- } else {
- *out_key = key;
- }
-
- if (out_certs == NULL) {
- if (certs != NULL) {
- CFRelease(certs);
- }
- } else {
- *out_certs = certs;
- }
- return status;
-}
-
-
-static OSStatus TryPEMImport(CFDataRef cfdata,
- CFStringRef password,
- CFArrayRef* out_certs,
- SecKeyRef* out_key) {
- OSStatus status = noErr;
-
- SecExternalFormat format = kSecFormatPEMSequence;
- SecExternalItemType sitem_type = kSecItemTypeAggregate;
-
- SecItemImportExportKeyParameters params;
- memset(¶ms, 0, sizeof(params));
- params.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
- params.flags = kSecKeyNoAccessControl;
- params.passphrase = password;
-
- CFArrayRef items = NULL;
- status = SecItemImport(cfdata, NULL, &format, &sitem_type, 0, ¶ms, NULL,
- &items);
-
- if (status != noErr) {
- if (SSL_LOG_CERTS) {
- Log::Print("TrySecItemImport failed with: %ld, type = %d, format = %d\n",
- static_cast<intptr_t>(status), sitem_type, format);
- }
- return status;
- }
-
- CFIndex items_length = (items == NULL) ? 0 : CFArrayGetCount(items);
- if (SSL_LOG_CERTS) {
- Log::Print(
- "TrySecItemImport succeeded, type = %d, format = %d, count = %ld\n",
- sitem_type, format, items_length);
- }
-
- // Empty list indicates a decoding failure of some sort.
- if ((items != NULL) && (items_length == 0)) {
- CFRelease(items);
- return errSSLBadCert;
- }
-
- status = GetKeyAndCerts(items, items_length, out_certs, out_key);
- CFRelease(items);
- return status;
-}
-
-
-static char* TempKeychainPath() {
- const char* exes = "keychaindir.XXXX";
- const char* fname = "keychain";
- const char* temp_dir = getenv("TMPDIR");
- if (temp_dir == NULL) {
- temp_dir = getenv("TMP");
- }
- if (temp_dir == NULL) {
- temp_dir = "/tmp/";
- }
- ASSERT(temp_dir != NULL);
-
- TextBuffer path(PATH_MAX);
- path.Printf("%s/%s", temp_dir, exes);
- char* ret = mkdtemp(path.buf());
- ASSERT(ret != NULL);
- path.Printf("/%s", fname);
-
- char* result = reinterpret_cast<char*>(Dart_ScopeAllocate(path.length() + 1));
- return strncpy(result, path.buf(), path.length() + 1);
-}
-
-
-static OSStatus CreateKeychain(SecKeychainRef* keychain) {
- ASSERT(keychain != NULL);
- OSStatus status = noErr;
- const char* temp_keychain_pwd = "dartdart";
- char* temp_file_path = TempKeychainPath();
- ASSERT(temp_file_path != NULL);
- if (SSL_LOG_CERTS) {
- Log::Print("Temporary keychain at: '%s'\n", temp_file_path);
- }
- status = SecKeychainCreate(temp_file_path, strlen(temp_keychain_pwd) + 1,
- reinterpret_cast<const void*>(temp_keychain_pwd),
- FALSE, // Prompt user? Definitely no.
- NULL, // Default access rights.
- keychain);
- if (status != noErr) {
- return status;
- }
- ASSERT(*keychain != NULL);
- return status;
-}
-
-
-static OSStatus TryPKCS12Import(CFDataRef cfdata,
- CFStringRef password,
- CFArrayRef* out_certs,
- SecKeyRef* out_key,
- SecKeychainRef* out_keychain) {
- OSStatus status = noErr;
-
- SecExternalFormat format = kSecFormatPKCS12;
- SecExternalItemType sitem_type = kSecItemTypeAggregate;
-
- SecItemImportExportKeyParameters params;
- memset(¶ms, 0, sizeof(params));
- params.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
- params.flags = kSecKeyNoAccessControl;
- params.passphrase = password;
-
- CFArrayRef items = NULL;
- if (SSL_LOG_CERTS) {
- Log::Print("Trying PKCS12 import with: type = %d, format = %d\n",
- sitem_type, format);
- }
-
- // The documentation for SecKeychainItemImport here:
- //
- // https://developer.apple.com/library/mac/documentation/Security/Reference/keychainservices/index.html
- //
- // states that when the SecKeychainRef argument is NULL, the CFArrayRef*
- // argument will be populated by an array containing all keys, identities,
- // and certificates from the data in the CFDataRef argument.
- //
- // Unfortunately, this is not true. The code to populate the CFArrayRef with
- // keys and identities from PKCS12 data has been skipped and/or commented out,
- // here:
- //
- // https://github.com/Apple-FOSS-Mirror/Security/blob/master/libsecurity_keychain/lib/SecImportExportAgg.cpp#L636
- //
- // as "floating" SecKeyRefs from the PKCS12 decoder haven't been implemented.
- // That is, each private key instance coming from the PKCS12 decoder has to be
- // associated with a keychain instance. Thus, as a workaround, we create a
- // temporary keychain here if one is needed, and stash it below in a
- // SecurityContext. This has the drawbacks:
- // 1.) We need to make a temporary directory to hold the keychain file, and
- // 2.) SecKeychainItemImport() probably does blocking IO to create and
- // manipulate the keychain file.
- // So if the API is updated, this keychain should not be used.
- SecKeychainRef keychain = NULL;
- if (out_key != NULL) {
- ASSERT(out_keychain != NULL);
- status = CreateKeychain(&keychain);
- if (status != noErr) {
- return status;
- }
- *out_keychain = keychain;
- }
-
- status = SecItemImport(cfdata, NULL, &format, &sitem_type, 0, ¶ms,
- keychain, &items);
- if (status != noErr) {
- if (SSL_LOG_CERTS) {
- Log::Print("TrySecItemImport failed with: %ld, it = %d, format = %d\n",
- static_cast<intptr_t>(status), sitem_type, format);
- }
- return status;
- }
-
- CFIndex items_length = (items == NULL) ? 0 : CFArrayGetCount(items);
- if (SSL_LOG_CERTS) {
- Log::Print("TrySecItemImport succeeded, count = %ld\n", items_length);
- }
-
- // Empty list indicates a decoding failure of some sort.
- if ((items != NULL) && (items_length == 0)) {
- CFRelease(items);
- return errSSLBadCert;
- }
-
- status = GetKeyAndCerts(items, items_length, out_certs, out_key);
- CFRelease(items);
- return status;
-}
-
-
-static OSStatus ExtractSecItems(uint8_t* buffer,
- intptr_t length,
- const char* password,
- CFArrayRef* out_certs,
- SecKeyRef* out_key,
- SecKeychainRef* out_keychain) {
- ASSERT(buffer != NULL);
- ASSERT(password != NULL);
- OSStatus status = noErr;
-
- CFDataRef cfdata =
- CFDataCreateWithBytesNoCopy(NULL, buffer, length, kCFAllocatorNull);
- CFStringRef cfpassword = CFStringCreateWithCStringNoCopy(
- NULL, password, kCFStringEncodingUTF8, kCFAllocatorNull);
- ASSERT(cfdata != NULL);
- ASSERT(cfpassword != NULL);
-
- status = TryPEMImport(cfdata, cfpassword, out_certs, out_key);
- if (status != noErr) {
- status =
- TryPKCS12Import(cfdata, cfpassword, out_certs, out_key, out_keychain);
- }
-
- CFRelease(cfdata);
- CFRelease(cfpassword);
- return status;
-}
-
-
-void FUNCTION_NAME(SecureSocket_Init)(Dart_NativeArguments args) {
- Dart_Handle dart_this = ThrowIfError(Dart_GetNativeArgument(args, 0));
- SSLFilter* filter = new SSLFilter(); // Deleted in DeleteFilter finalizer.
- 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 dart_this = ThrowIfError(Dart_GetNativeArgument(args, 0));
- 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));
-
- 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, kSecurityContextNativeFieldIndex,
- reinterpret_cast<intptr_t*>(&context)));
- }
-
- GetFilter(args)->Connect(dart_this, host_name, context, is_server,
- request_client_certificate,
- require_client_certificate);
-}
-
-
-void FUNCTION_NAME(SecureSocket_Destroy)(Dart_NativeArguments args) {
- SSLFilter* filter = GetFilter(args);
- // The SSLFilter is deleted in the finalizer for the Dart object created by
- // SetFilter. There is no need to NULL-out the native field for the SSLFilter
- // here because the SSLFilter won't be deleted until the finalizer for the
- // Dart object runs while the Dart object is being GCd. This approach avoids a
- // leak if Destroy isn't called, and avoids a NULL-dereference if Destroy is
- // called more than once.
- filter->Destroy();
-}
-
-
-void FUNCTION_NAME(SecureSocket_Handshake)(Dart_NativeArguments args) {
- OSStatus status = GetFilter(args)->CheckHandshake();
- CheckStatus(status, "HandshakeException", "Handshake error");
-}
-
-
-void FUNCTION_NAME(SecureSocket_GetSelectedProtocol)(
- Dart_NativeArguments args) {
- Dart_SetReturnValue(args, Dart_Null());
-}
-
-
-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_SetReturnValue(args, GetFilter(args)->PeerCertificate());
-}
-
-
-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));
-}
-
-
-void FUNCTION_NAME(SecurityContext_Allocate)(Dart_NativeArguments args) {
- SSLCertContext* cert_context = new SSLCertContext();
- // cert_context deleted in DeleteCertContext finalizer.
- Dart_Handle err = SetSecurityContext(args, cert_context);
- if (Dart_IsError(err)) {
- cert_context->Release();
- Dart_PropagateError(err);
- }
-}
-
-
-void FUNCTION_NAME(SecurityContext_UsePrivateKeyBytes)(
- Dart_NativeArguments args) {
- SSLCertContext* context = GetSecurityContext(args);
- const char* password = GetPasswordArgument(args, 2);
-
- OSStatus status;
- SecKeyRef key = NULL;
- SecKeychainRef keychain = NULL;
- {
- ScopedMemBuffer buffer(ThrowIfError(Dart_GetNativeArgument(args, 1)));
- status = ExtractSecItems(buffer.get(), buffer.length(), password, NULL,
- &key, &keychain);
- }
-
- // Set the context fields. If there's a failure, release the items.
- bool set_failure = false;
- if ((key != NULL) && !context->set_private_key(key)) {
- CFRelease(key);
- SecKeychainDelete(keychain);
- CFRelease(keychain);
- set_failure = true;
- }
- if (!set_failure && (keychain != NULL) && !context->set_keychain(keychain)) {
- SecKeychainDelete(keychain);
- CFRelease(keychain);
- }
-
- if (set_failure) {
- Dart_ThrowException(DartUtils::NewDartArgumentError(
- "usePrivateKeyBytes has already been called on the given context."));
- }
- CheckStatus(status, "TlsException", "Failure in usePrivateKeyBytes");
-}
-
-
-void FUNCTION_NAME(SecurityContext_SetTrustedCertificatesBytes)(
- Dart_NativeArguments args) {
- SSLCertContext* context = GetSecurityContext(args);
- const char* password = GetPasswordArgument(args, 2);
-
- OSStatus status;
- CFArrayRef certs = NULL;
- {
- ScopedMemBuffer buffer(ThrowIfError(Dart_GetNativeArgument(args, 1)));
- status = ExtractSecItems(buffer.get(), buffer.length(), password, &certs,
- NULL, NULL);
- }
-
- // Set the field in the context. If there's a failure, release the certs,
- // and throw an exception.
- if ((certs != NULL) && !context->set_trusted_certs(certs)) {
- CFRelease(certs);
- Dart_ThrowException(DartUtils::NewDartArgumentError(
- "setTrustedCertificatesBytes has already been called "
- "on the given context."));
- }
-
- CheckStatus(status, "TlsException", "Failure in setTrustedCertificatesBytes");
-}
-
-
-void FUNCTION_NAME(SecurityContext_AlpnSupported)(Dart_NativeArguments args) {
- Dart_SetReturnValue(args, Dart_NewBoolean(false));
-}
-
-
-void FUNCTION_NAME(SecurityContext_TrustBuiltinRoots)(
- Dart_NativeArguments args) {
- SSLCertContext* context = GetSecurityContext(args);
- context->set_trust_builtin(true);
-}
-
-
-void FUNCTION_NAME(SecurityContext_UseCertificateChainBytes)(
- Dart_NativeArguments args) {
- SSLCertContext* context = GetSecurityContext(args);
-
- const char* password = GetPasswordArgument(args, 2);
- OSStatus status;
- CFArrayRef certs = NULL;
- {
- ScopedMemBuffer buffer(ThrowIfError(Dart_GetNativeArgument(args, 1)));
- status = ExtractSecItems(buffer.get(), buffer.length(), password, &certs,
- NULL, NULL);
- }
-
- // Set the field in the context. If there's a failure, release the certs,
- // and throw an exception.
- if ((certs != NULL) && !context->set_cert_chain(certs)) {
- CFRelease(certs);
- Dart_ThrowException(DartUtils::NewDartArgumentError(
- "useCertificateChainBytes has already been called "
- "on the given context."));
- }
-
- CheckStatus(status, "TlsException", "Failure in useCertificateChainBytes");
-}
-
-
-void FUNCTION_NAME(SecurityContext_SetClientAuthoritiesBytes)(
- Dart_NativeArguments args) {
- SSLCertContext* context = GetSecurityContext(args);
- const char* password = GetPasswordArgument(args, 2);
-
- OSStatus status;
- CFArrayRef certs = NULL;
- {
- ScopedMemBuffer buffer(ThrowIfError(Dart_GetNativeArgument(args, 1)));
- status = ExtractSecItems(buffer.get(), buffer.length(), password, &certs,
- NULL, NULL);
- }
-
- // Set the field in the context. If there's a failure, release the certs,
- // and throw an exception.
- if ((certs != NULL) && !context->set_cert_authorities(certs)) {
- CFRelease(certs);
- Dart_ThrowException(DartUtils::NewDartArgumentError(
- "setClientAuthoritiesBytes has already been called "
- "on the given context."));
- }
-
- CheckStatus(status, "TlsException", "Failure in setClientAuthoritiesBytes");
-}
-
-
-void FUNCTION_NAME(SecurityContext_SetAlpnProtocols)(
- Dart_NativeArguments args) {
- Dart_ThrowException(DartUtils::NewDartUnsupportedError(
- "ALPN is not supported on this platform"));
-}
-
-
-static char* GetNameFromCert(SecCertificateRef certificate,
- CFTypeRef field,
- CFStringRef name) {
- char* issuer_name = NULL;
-
- CFTypeRef keys[] = {field};
- CFArrayRef key_array = CFArrayCreate(NULL, keys, 1, &kCFTypeArrayCallBacks);
- CFErrorRef error = NULL;
- CFDictionaryRef cert_dict =
- SecCertificateCopyValues(certificate, key_array, &error);
- if (cert_dict == NULL) {
- CFRelease(key_array);
- Dart_ThrowException(DartUtils::NewDartArgumentError(
- "X509.issuer failed to copy issuer field out of certificate"));
- }
-
- CFTypeRef item = CFDictionaryGetValue(cert_dict, keys[0]);
- ASSERT(CFGetTypeID(item) == CFDictionaryGetTypeID());
- CFDictionaryRef val_dict = reinterpret_cast<CFDictionaryRef>(item);
-
- item = CFDictionaryGetValue(val_dict, kSecPropertyKeyValue);
- ASSERT(CFGetTypeID(item) == CFArrayGetTypeID());
- CFArrayRef val_array = reinterpret_cast<CFArrayRef>(item);
-
- for (intptr_t i = 0; i < CFArrayGetCount(val_array); i++) {
- item = CFArrayGetValueAtIndex(val_array, i);
- ASSERT(CFGetTypeID(item) == CFDictionaryGetTypeID());
- CFDictionaryRef val_dict2 = reinterpret_cast<CFDictionaryRef>(item);
-
- item = CFDictionaryGetValue(val_dict2, kSecPropertyKeyLabel);
- ASSERT(CFGetTypeID(item) == CFStringGetTypeID());
- CFStringRef label = reinterpret_cast<CFStringRef>(item);
-
- if (CFStringCompare(label, name, 0) == kCFCompareEqualTo) {
- item = CFDictionaryGetValue(val_dict2, kSecPropertyKeyValue);
- ASSERT(CFGetTypeID(item) == CFStringGetTypeID());
- CFStringRef value = reinterpret_cast<CFStringRef>(item);
- issuer_name = CFStringRefToCString(value);
- break;
- }
- }
-
- CFRelease(cert_dict);
- CFRelease(key_array);
- return issuer_name;
-}
-
-
-void FUNCTION_NAME(X509_Subject)(Dart_NativeArguments args) {
- SecCertificateRef certificate = GetX509Certificate(args);
- char* subject_name =
- GetNameFromCert(certificate, kSecOIDX509V1SubjectName,
- reinterpret_cast<CFStringRef>(kSecOIDCommonName));
- if (subject_name == NULL) {
- Dart_ThrowException(DartUtils::NewDartArgumentError(
- "X509.subject failed to find subject's common name."));
- } else {
- Dart_SetReturnValue(args, Dart_NewStringFromCString(subject_name));
- }
-}
-
-
-void FUNCTION_NAME(X509_Issuer)(Dart_NativeArguments args) {
- SecCertificateRef certificate = GetX509Certificate(args);
- char* issuer_name =
- GetNameFromCert(certificate, kSecOIDX509V1IssuerName,
- reinterpret_cast<CFStringRef>(kSecOIDCommonName));
- if (issuer_name == NULL) {
- Dart_ThrowException(DartUtils::NewDartArgumentError(
- "X509.issuer failed to find issuer's common name."));
- } else {
- Dart_SetReturnValue(args, Dart_NewStringFromCString(issuer_name));
- }
-}
-
-
-// Returns the number of seconds since the epoch from 'field'.
-static int64_t GetTimeFromCert(SecCertificateRef certificate, CFTypeRef field) {
- CFTypeRef keys[] = {field};
- CFArrayRef key_array = CFArrayCreate(NULL, keys, 1, &kCFTypeArrayCallBacks);
- CFErrorRef error = NULL;
- CFDictionaryRef cert_dict =
- SecCertificateCopyValues(certificate, key_array, &error);
- if (cert_dict == NULL) {
- CFRelease(key_array);
- Dart_ThrowException(DartUtils::NewDartArgumentError(
- "X509.startValidity: failed to copy issuer field out of certificate"));
- }
-
- CFTypeRef item = CFDictionaryGetValue(cert_dict, keys[0]);
- ASSERT(CFGetTypeID(item) == CFDictionaryGetTypeID());
- CFDictionaryRef val_dict = reinterpret_cast<CFDictionaryRef>(item);
-
- item = CFDictionaryGetValue(val_dict, kSecPropertyKeyValue);
- ASSERT(CFGetTypeID(item) == CFNumberGetTypeID());
- CFNumberRef date_number = reinterpret_cast<CFNumberRef>(item);
-
- CFAbsoluteTime date_abs_time;
- CFNumberGetValue(date_number, kCFNumberDoubleType, &date_abs_time);
- CFAbsoluteTime seconds_since_epoch =
- date_abs_time + kCFAbsoluteTimeIntervalSince1970;
- return static_cast<int64_t>(seconds_since_epoch) * 1000LL;
-}
-
-
-void FUNCTION_NAME(X509_StartValidity)(Dart_NativeArguments args) {
- SecCertificateRef certificate = GetX509Certificate(args);
- int64_t seconds_since_epoch =
- GetTimeFromCert(certificate, kSecOIDX509V1ValidityNotBefore);
- Dart_SetReturnValue(
- args,
- Dart_NewInteger(static_cast<int64_t>(seconds_since_epoch) * 1000LL));
-}
-
-
-void FUNCTION_NAME(X509_EndValidity)(Dart_NativeArguments args) {
- SecCertificateRef certificate = GetX509Certificate(args);
- int64_t seconds_since_epoch =
- GetTimeFromCert(certificate, kSecOIDX509V1ValidityNotAfter);
- Dart_SetReturnValue(
- args,
- Dart_NewInteger(static_cast<int64_t>(seconds_since_epoch) * 1000LL));
-}
-
-
-// Pushes data through the SSL filter, reading and writing from circular
-// buffers shared with Dart. Called from the IOService thread.
-//
-// 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();
- intptr_t starts[SSLFilter::kNumBuffers];
- intptr_t ends[SSLFilter::kNumBuffers];
- for (intptr_t i = 0; i < SSLFilter::kNumBuffers; ++i) {
- starts[i] = CObjectInt32(request[2 * i + 2]).Value();
- ends[i] = CObjectInt32(request[2 * i + 3]).Value();
- }
-
- OSStatus status = filter->ProcessAllBuffers(starts, ends, in_handshake);
- if (status == noErr) {
- CObjectArray* result =
- new CObjectArray(CObject::NewArray(SSLFilter::kNumBuffers * 2));
- for (intptr_t 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 {
- TextBuffer status_message(SSL_ERROR_MESSAGE_BUFFER_SIZE);
- CFStringRef error_string = SecCopyErrorMessageString(status, NULL);
- if (error_string == NULL) {
- status_message.Printf("OSStatus = %ld: https://www.osstatus.com",
- static_cast<intptr_t>(status));
- } else {
- char* error = CFStringRefToCString(error_string);
- status_message.Printf("OSStatus = %ld: %s", static_cast<intptr_t>(status),
- error);
- CFRelease(error_string);
- }
- CObjectArray* result = new CObjectArray(CObject::NewArray(2));
- result->SetAt(0, new CObjectInt32(CObject::NewInt32(status)));
- result->SetAt(1,
- new CObjectString(CObject::NewString(status_message.buf())));
- return result;
- }
-}
-
-
-// Usually buffer_starts_ and buffer_ends_ are populated by ProcessAllBuffers,
-// called from ProcessFilterRequest, called from the IOService thread.
-// However, the first call to SSLHandshake comes from the Dart thread, and so
-// doesn't go through there. This results in calls to SSLReadCallback and
-// SSLWriteCallback in which buffer_starts_ and buffer_ends_ haven't been set
-// up. In that case, since we're coming from Dart anyway, we can access the
-// fieds directly from the Dart objects.
-intptr_t SSLFilter::GetBufferStart(intptr_t idx) const {
- if (buffer_starts_[idx] != NULL) {
- return *buffer_starts_[idx];
- }
- Dart_Handle buffer_handle =
- ThrowIfError(Dart_HandleFromPersistent(dart_buffer_objects_[idx]));
- Dart_Handle start_handle =
- ThrowIfError(Dart_GetField(buffer_handle, DartUtils::NewString("start")));
- int64_t start = DartUtils::GetIntegerValue(start_handle);
- return static_cast<intptr_t>(start);
-}
-
-
-intptr_t SSLFilter::GetBufferEnd(intptr_t idx) const {
- if (buffer_ends_[idx] != NULL) {
- return *buffer_ends_[idx];
- }
- Dart_Handle buffer_handle =
- ThrowIfError(Dart_HandleFromPersistent(dart_buffer_objects_[idx]));
- Dart_Handle end_handle =
- ThrowIfError(Dart_GetField(buffer_handle, DartUtils::NewString("end")));
- int64_t end = DartUtils::GetIntegerValue(end_handle);
- return static_cast<intptr_t>(end);
-}
-
-
-void SSLFilter::SetBufferStart(intptr_t idx, intptr_t value) {
- if (buffer_starts_[idx] != NULL) {
- *buffer_starts_[idx] = value;
- return;
- }
- Dart_Handle buffer_handle =
- ThrowIfError(Dart_HandleFromPersistent(dart_buffer_objects_[idx]));
- ThrowIfError(DartUtils::SetIntegerField(buffer_handle, "start",
- static_cast<int64_t>(value)));
-}
-
-
-void SSLFilter::SetBufferEnd(intptr_t idx, intptr_t value) {
- if (buffer_ends_[idx] != NULL) {
- *buffer_ends_[idx] = value;
- return;
- }
- Dart_Handle buffer_handle =
- ThrowIfError(Dart_HandleFromPersistent(dart_buffer_objects_[idx]));
- ThrowIfError(DartUtils::SetIntegerField(buffer_handle, "end",
- static_cast<int64_t>(value)));
-}
-
-
-OSStatus SSLFilter::ProcessAllBuffers(intptr_t starts[kNumBuffers],
- intptr_t ends[kNumBuffers],
- bool in_handshake) {
- for (intptr_t i = 0; i < kNumBuffers; ++i) {
- buffer_starts_[i] = &starts[i];
- buffer_ends_[i] = &ends[i];
- }
-
- if (in_handshake) {
- OSStatus status = Handshake();
- if (status != noErr) {
- return status;
- }
- } else {
- for (intptr_t i = 0; i < kNumBuffers; ++i) {
- intptr_t start = starts[i];
- intptr_t end = ends[i];
- intptr_t 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:
- // 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).
- intptr_t buffer_end = (start == 0) ? size - 1 : size;
- intptr_t bytes = 0;
- OSStatus status =
- ProcessReadPlaintextBuffer(end, buffer_end, &bytes);
- if ((status != noErr) && (status != errSSLWouldBlock)) {
- return status;
- }
- end += bytes;
- ASSERT(end <= size);
- if (end == size) {
- end = 0;
- }
- }
- if (start > end + 1) {
- intptr_t bytes = 0;
- OSStatus status =
- ProcessReadPlaintextBuffer(end, start - 1, &bytes);
- if ((status != noErr) && (status != errSSLWouldBlock)) {
- return status;
- }
- end += bytes;
- ASSERT(end < start);
- }
- ends[i] = end;
- break;
- 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).
- intptr_t bytes = 0;
- OSStatus status = ProcessWritePlaintextBuffer(start, size, &bytes);
- if ((status != noErr) && (status != errSSLWouldBlock)) {
- return status;
- }
- start += bytes;
- ASSERT(start <= size);
- if (start == size) {
- start = 0;
- }
- }
- if (start < end) {
- intptr_t bytes = 0;
- OSStatus status = ProcessWritePlaintextBuffer(start, end, &bytes);
- if ((status != noErr) && (status != errSSLWouldBlock)) {
- return status;
- }
- start += bytes;
- ASSERT(start <= end);
- }
- starts[i] = start;
- break;
- case kReadEncrypted:
- case kWriteEncrypted:
- // These buffers are advanced through SSLReadCallback and
- // SSLWriteCallback, which are called from SSLRead, SSLWrite, and
- // SSLHandshake.
- break;
- default:
- UNREACHABLE();
- }
- }
- }
-
- for (intptr_t i = 0; i < kNumBuffers; ++i) {
- buffer_starts_[i] = NULL;
- buffer_ends_[i] = NULL;
- }
- return noErr;
-}
-
-
-Dart_Handle SSLFilter::Init(Dart_Handle dart_this) {
- 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<intptr_t>(buffer_size);
- encrypted_buffer_size_ = static_cast<intptr_t>(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);
- buffer_starts_[i] = NULL;
- buffer_ends_[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() {
- if (peer_certs_ == NULL) {
- return Dart_Null();
- }
-
- CFTypeRef item = CFArrayGetValueAtIndex(peer_certs_, 0);
- ASSERT(CFGetTypeID(item) == SecCertificateGetTypeID());
- SecCertificateRef cert =
- reinterpret_cast<SecCertificateRef>(const_cast<void*>(item));
- if (cert == NULL) {
- return Dart_Null();
- }
-
- return WrappedX509Certificate(cert);
-}
-
-
-void SSLFilter::Connect(Dart_Handle dart_this,
- const char* hostname,
- SSLCertContext* context,
- bool is_server,
- bool request_client_certificate,
- bool require_client_certificate) {
- if (in_handshake_) {
- FATAL("Connect called twice on the same _SecureFilter.");
- }
-
- // Create the underlying context
- SSLContextRef ssl_context = SSLCreateContext(
- NULL, is_server ? kSSLServerSide : kSSLClientSide, kSSLStreamType);
-
- // Configure the context.
- OSStatus status;
- status = SSLSetPeerDomainName(ssl_context, hostname, strlen(hostname));
- CheckStatus(status, "TlsException", "Failed to set peer domain name");
-
- status = SSLSetIOFuncs(ssl_context, SSLFilter::SSLReadCallback,
- SSLFilter::SSLWriteCallback);
- CheckStatus(status, "TlsException", "Failed to set IO Callbacks");
-
- status =
- SSLSetConnection(ssl_context, reinterpret_cast<SSLConnectionRef>(this));
- CheckStatus(status, "TlsException", "Failed to set connection object");
-
- // Always evaluate the certs manually so that we can cache the peer
- // certificates in the context for calls to peerCertificate.
- status = SSLSetSessionOption(ssl_context, kSSLSessionOptionBreakOnServerAuth,
- true);
- CheckStatus(status, "TlsException", "Failed to set BreakOnServerAuth option");
-
- status = SSLSetProtocolVersionMin(ssl_context, kTLSProtocol1);
- CheckStatus(status, "TlsException",
- "Failed to set minimum protocol version to kTLSProtocol1");
-
- // If the context has a private key and certificate chain, combine the
- // private key and first certificate into a SecIdentityRef, and place that
- // and the remaining certs in an array to pass to SSLSetCertificate().
- if ((context->private_key() != NULL) && (context->cert_chain() != NULL)) {
- CFIndex chain_length = CFArrayGetCount(context->cert_chain());
- CFMutableArrayRef certs =
- CFArrayCreateMutable(NULL, chain_length, &kCFTypeArrayCallBacks);
- CFTypeRef item = CFArrayGetValueAtIndex(context->cert_chain(), 0);
- ASSERT(CFGetTypeID(item) == SecCertificateGetTypeID());
- SecCertificateRef first_cert =
- reinterpret_cast<SecCertificateRef>(const_cast<void*>(item));
- SecIdentityRef identity =
- SecIdentityCreate(NULL, first_cert, context->private_key());
- CFArrayAppendValue(certs, identity);
- for (CFIndex i = 0; i < chain_length; i++) {
- CFArrayAppendValue(certs,
- CFArrayGetValueAtIndex(context->cert_chain(), i));
- }
- CFRelease(identity);
- status = SSLSetCertificate(ssl_context, certs);
- CFRelease(certs);
- CheckStatus(status, "TlsException", "SSLSetCertificate failed");
- }
-
- if (context->cert_authorities() != NULL) {
- status = SSLSetCertificateAuthorities(ssl_context,
- context->cert_authorities(), true);
- CheckStatus(status, "TlsException",
- "Failed to set certificate authorities");
- }
-
- if (is_server) {
- SSLAuthenticate auth =
- require_client_certificate
- ? kAlwaysAuthenticate
- : (request_client_certificate ? kTryAuthenticate
- : kNeverAuthenticate);
- status = SSLSetClientSideAuthenticate(ssl_context, auth);
- CheckStatus(status, "TlsException",
- "Failed to set client authentication mode");
-
- // If we're at least trying client authentication, then break handshake
- // for client authentication.
- if (auth != kNeverAuthenticate) {
- status = SSLSetSessionOption(ssl_context,
- kSSLSessionOptionBreakOnClientAuth, true);
- CheckStatus(status, "TlsException",
- "Failed to set client authentication mode");
- }
- }
-
- // Add the contexts to our wrapper.
- cert_context_.set(context);
- ssl_context_ = ssl_context;
- is_server_ = is_server;
-
- // Kick-off the handshake. Expect the handshake to need more data.
- // SSLHandshake calls our SSLReadCallback and SSLWriteCallback.
- status = SSLHandshake(ssl_context);
- ASSERT(status != noErr);
- if (status == errSSLWouldBlock) {
- status = noErr;
- in_handshake_ = true;
- }
- CheckStatus(status, "HandshakeException", is_server_
- ? "Handshake error in server"
- : "Handshake error in client");
-}
-
-
-OSStatus SSLFilter::EvaluatePeerTrust() {
- OSStatus status = noErr;
-
- if (SSL_LOG_STATUS) {
- Log::Print("Handshake evaluating trust.\n");
- }
- SecTrustRef peer_trust = NULL;
- status = SSLCopyPeerTrust(ssl_context_, &peer_trust);
- if (status != noErr) {
- if (is_server_ && (status == errSSLBadCert)) {
- // A client certificate was requested, but not required, and wasn't sent.
- return noErr;
- }
- if (SSL_LOG_STATUS) {
- Log::Print("Handshake error from SSLCopyPeerTrust(): %ld.\n",
- static_cast<intptr_t>(status));
- }
- return status;
- }
-
- CFArrayRef trusted_certs = NULL;
- if (cert_context_.get()->trusted_certs() != NULL) {
- trusted_certs =
- CFArrayCreateCopy(NULL, cert_context_.get()->trusted_certs());
- } else {
- trusted_certs = CFArrayCreate(NULL, NULL, 0, &kCFTypeArrayCallBacks);
- }
-
- status = SecTrustSetAnchorCertificates(peer_trust, trusted_certs);
- if (status != noErr) {
- if (SSL_LOG_STATUS) {
- Log::Print("Handshake error from SecTrustSetAnchorCertificates: %ld\n",
- static_cast<intptr_t>(status));
- }
- CFRelease(trusted_certs);
- CFRelease(peer_trust);
- return status;
- }
-
- if (SSL_LOG_STATUS) {
- Log::Print(
- "Handshake %s built in root certs\n",
- cert_context_.get()->trust_builtin() ? "trusting" : "not trusting");
- }
-
- status = SecTrustSetAnchorCertificatesOnly(
- peer_trust, !cert_context_.get()->trust_builtin());
- if (status != noErr) {
- CFRelease(trusted_certs);
- CFRelease(peer_trust);
- return status;
- }
-
- SecTrustResultType trust_result;
- status = SecTrustEvaluate(peer_trust, &trust_result);
- if (status != noErr) {
- CFRelease(trusted_certs);
- CFRelease(peer_trust);
- return status;
- }
-
- // Grab the peer's certificate chain.
- CFIndex peer_chain_length = SecTrustGetCertificateCount(peer_trust);
- CFMutableArrayRef peer_certs =
- CFArrayCreateMutable(NULL, peer_chain_length, &kCFTypeArrayCallBacks);
- for (CFIndex i = 0; i < peer_chain_length; ++i) {
- CFArrayAppendValue(peer_certs,
- SecTrustGetCertificateAtIndex(peer_trust, i));
- }
- peer_certs_ = peer_certs;
-
- CFRelease(trusted_certs);
- CFRelease(peer_trust);
-
- if ((trust_result == kSecTrustResultProceed) ||
- (trust_result == kSecTrustResultUnspecified)) {
- // Trusted.
- return noErr;
- } else {
- if (SSL_LOG_STATUS) {
- Log::Print("Trust eval failed: trust_restul = %d\n", trust_result);
- }
- bad_cert_ = true;
- return errSSLBadCert;
- }
-}
-
-
-OSStatus SSLFilter::Handshake() {
- ASSERT(cert_context_.get() != NULL);
- ASSERT(ssl_context_ != NULL);
- // Try and push handshake along.
- if (SSL_LOG_STATUS) {
- Log::Print("Doing SSLHandshake\n");
- }
- OSStatus status = SSLHandshake(ssl_context_);
- if (SSL_LOG_STATUS) {
- Log::Print("SSLHandshake returned %ld\n", static_cast<intptr_t>(status));
- }
-
- if ((status == errSSLServerAuthCompleted) ||
- (status == errSSLClientAuthCompleted)) {
- status = EvaluatePeerTrust();
- if (status == errSSLBadCert) {
- // Need to invoke the bad certificate callback.
- return noErr;
- } else if (status != noErr) {
- return status;
- }
- // When trust evaluation succeeds, we can call SSLHandshake again
- // immediately.
- status = SSLHandshake(ssl_context_);
- }
-
- if (status == errSSLWouldBlock) {
- in_handshake_ = true;
- return noErr;
- }
-
- // Handshake succeeded.
- if ((in_handshake_) && (status == noErr)) {
- if (SSL_LOG_STATUS) {
- Log::Print("Finished with the Handshake\n");
- }
- connected_ = true;
- }
- return status;
-}
-
-
-// Returns false if Handshake should fail, and true if Handshake should
-// proceed.
-Dart_Handle SSLFilter::InvokeBadCertCallback(SecCertificateRef peer_cert) {
- Dart_Handle callback = bad_certificate_callback_;
- if (Dart_IsNull(callback)) {
- return callback;
- }
- Dart_Handle args[1];
- args[0] = WrappedX509Certificate(peer_cert);
- if (Dart_IsError(args[0])) {
- return args[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()));
- }
- return result;
-}
-
-
-OSStatus SSLFilter::CheckHandshake() {
- if (bad_cert_ && in_handshake_) {
- if (SSL_LOG_STATUS) {
- Log::Print("Invoking bad certificate callback\n");
- }
- ASSERT(peer_certs_ != NULL);
- CFIndex peer_certs_len = CFArrayGetCount(peer_certs_);
- ASSERT(peer_certs_len > 0);
- CFTypeRef item = CFArrayGetValueAtIndex(peer_certs_, peer_certs_len - 1);
- ASSERT(item != NULL);
- ASSERT(CFGetTypeID(item) == SecCertificateGetTypeID());
- SecCertificateRef peer_cert =
- reinterpret_cast<SecCertificateRef>(const_cast<void*>(item));
- Dart_Handle result = InvokeBadCertCallback(peer_cert);
- ThrowIfError(result);
- if (Dart_IsNull(result)) {
- return errSSLBadCert;
- } else {
- bool good_cert = DartUtils::GetBooleanValue(result);
- bad_cert_ = !good_cert;
- return good_cert ? noErr : errSSLBadCert;
- }
- }
-
- if (connected_ && in_handshake_) {
- if (SSL_LOG_STATUS) {
- Log::Print("Invoking handshake complete callback\n");
- }
- ThrowIfError(Dart_InvokeClosure(
- Dart_HandleFromPersistent(handshake_complete_), 0, NULL));
- in_handshake_ = false;
- }
- return noErr;
-}
-
-
-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.
-}
-
-
-SSLFilter::~SSLFilter() {
- if (ssl_context_ != NULL) {
- CFRelease(ssl_context_);
- ssl_context_ = NULL;
- }
- if (peer_certs_ != NULL) {
- CFRelease(peer_certs_);
- peer_certs_ = 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;
- }
- }
-}
-
-
-void SSLFilter::Destroy() {
- if (ssl_context_ != NULL) {
- SSLClose(ssl_context_);
- }
- 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;
- }
-}
-
-
-OSStatus SSLFilter::SSLReadCallback(SSLConnectionRef connection,
- void* data,
- size_t* data_requested) {
- // Copy at most `data_requested` bytes from `buffers_[kReadEncrypted]` into
- // `data`
- ASSERT(connection != NULL);
- ASSERT(data != NULL);
- ASSERT(data_requested != NULL);
-
- SSLFilter* filter =
- const_cast<SSLFilter*>(reinterpret_cast<const SSLFilter*>(connection));
- uint8_t* datap = reinterpret_cast<uint8_t*>(data);
- uint8_t* buffer = filter->buffers_[kReadEncrypted];
- intptr_t start = filter->GetBufferStart(kReadEncrypted);
- intptr_t end = filter->GetBufferEnd(kReadEncrypted);
- intptr_t size = filter->encrypted_buffer_size_;
- intptr_t requested = static_cast<intptr_t>(*data_requested);
- intptr_t data_read = 0;
-
- if (end < start) {
- // Data may be split into two segments. In this case,
- // the first is [start, size).
- intptr_t buffer_end = (start == 0) ? size - 1 : size;
- intptr_t available = buffer_end - start;
- intptr_t bytes = requested < available ? requested : available;
- memmove(datap, &buffer[start], bytes);
- start += bytes;
- datap += bytes;
- data_read += bytes;
- requested -= bytes;
- ASSERT(start <= size);
- if (start == size) {
- start = 0;
- }
- }
- if ((requested > 0) && (start < end)) {
- intptr_t available = end - start;
- intptr_t bytes = requested < available ? requested : available;
- memmove(datap, &buffer[start], bytes);
- start += bytes;
- datap += bytes;
- data_read += bytes;
- requested -= bytes;
- ASSERT(start <= end);
- }
-
- if (SSL_LOG_DATA) {
- Log::Print("SSLReadCallback: requested: %ld, read %ld bytes\n",
- *data_requested, data_read);
- }
-
- filter->SetBufferStart(kReadEncrypted, start);
- bool short_read = data_read < static_cast<intptr_t>(*data_requested);
- *data_requested = data_read;
- return short_read ? errSSLWouldBlock : noErr;
-}
-
-
-// Read decrypted data from the filter to the circular buffer.
-OSStatus SSLFilter::ProcessReadPlaintextBuffer(intptr_t start,
- intptr_t end,
- intptr_t* bytes_processed) {
- ASSERT(bytes_processed != NULL);
- intptr_t length = end - start;
- OSStatus status = noErr;
- size_t bytes = 0;
- if (length > 0) {
- status =
- SSLRead(ssl_context_,
- reinterpret_cast<void*>((buffers_[kReadPlaintext] + start)),
- length, &bytes);
- if (SSL_LOG_STATUS) {
- Log::Print("SSLRead: status = %ld\n", static_cast<intptr_t>(status));
- }
- if ((status != noErr) && (status != errSSLWouldBlock)) {
- *bytes_processed = 0;
- return status;
- }
- }
- if (SSL_LOG_DATA) {
- Log::Print("ProcessReadPlaintextBuffer: requested: %ld, read %ld bytes\n",
- length, bytes);
- }
- *bytes_processed = static_cast<intptr_t>(bytes);
- return status;
-}
-
-
-OSStatus SSLFilter::SSLWriteCallback(SSLConnectionRef connection,
- const void* data,
- size_t* data_provided) {
- // Copy at most `data_provided` bytes from data into
- // `buffers_[kWriteEncrypted]`.
- ASSERT(connection != NULL);
- ASSERT(data != NULL);
- ASSERT(data_provided != NULL);
-
- SSLFilter* filter =
- const_cast<SSLFilter*>(reinterpret_cast<const SSLFilter*>(connection));
- const uint8_t* datap = reinterpret_cast<const uint8_t*>(data);
- uint8_t* buffer = filter->buffers_[kWriteEncrypted];
- intptr_t start = filter->GetBufferStart(kWriteEncrypted);
- intptr_t end = filter->GetBufferEnd(kWriteEncrypted);
- intptr_t size = filter->encrypted_buffer_size_;
- intptr_t provided = static_cast<intptr_t>(*data_provided);
- intptr_t data_written = 0;
-
- // 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).
- intptr_t buffer_end = (start == 0) ? size - 1 : size;
- intptr_t available = buffer_end - end;
- intptr_t bytes = provided < available ? provided : available;
- memmove(&buffer[end], datap, bytes);
- end += bytes;
- datap += bytes;
- data_written += bytes;
- provided -= bytes;
- ASSERT(end <= size);
- if (end == size) {
- end = 0;
- }
- }
- if ((provided > 0) && (start > end + 1)) {
- intptr_t available = (start - 1) - end;
- intptr_t bytes = provided < available ? provided : available;
- memmove(&buffer[end], datap, bytes);
- end += bytes;
- datap += bytes;
- data_written += bytes;
- provided -= bytes;
- ASSERT(end < start);
- }
-
- if (SSL_LOG_DATA) {
- Log::Print("SSLWriteCallback: provided: %ld, written %ld bytes\n",
- *data_provided, data_written);
- }
-
- filter->SetBufferEnd(kWriteEncrypted, end);
- *data_provided = data_written;
- return (data_written == 0) ? errSSLWouldBlock : noErr;
-}
-
-
-OSStatus SSLFilter::ProcessWritePlaintextBuffer(intptr_t start,
- intptr_t end,
- intptr_t* bytes_processed) {
- ASSERT(bytes_processed != NULL);
- intptr_t length = end - start;
- OSStatus status = noErr;
- size_t bytes = 0;
- if (length > 0) {
- status =
- SSLWrite(ssl_context_,
- reinterpret_cast<void*>(buffers_[kWritePlaintext] + start),
- length, &bytes);
- if (SSL_LOG_STATUS) {
- Log::Print("SSLWrite: status = %ld\n", static_cast<intptr_t>(status));
- }
- if ((status != noErr) && (status != errSSLWouldBlock)) {
- *bytes_processed = 0;
- return status;
- }
- }
- if (SSL_LOG_DATA) {
- Log::Print("ProcessWritePlaintextBuffer: requested: %ld, written: %ld\n",
- length, bytes);
- }
- *bytes_processed = static_cast<intptr_t>(bytes);
- return status;
-}
-
-} // namespace bin
-} // namespace dart
-
-#endif // defined(HOST_OS_MACOS) && !HOST_OS_IOS
-
-#endif // !defined(DART_IO_DISABLED) &&
- // !defined(DART_IO_SECURE_SOCKET_DISABLED)
diff --git a/runtime/bin/secure_socket_macos.h b/runtime/bin/secure_socket_macos.h
deleted file mode 100644
index f84cc42..0000000
--- a/runtime/bin/secure_socket_macos.h
+++ /dev/null
@@ -1,279 +0,0 @@
-// Copyright (c) 2016, 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.
-
-#ifndef RUNTIME_BIN_SECURE_SOCKET_MACOS_H_
-#define RUNTIME_BIN_SECURE_SOCKET_MACOS_H_
-
-#if !defined(RUNTIME_BIN_SECURE_SOCKET_H_)
-#error Do not include secure_socket_macos.h directly. Use secure_socket.h.
-#endif
-
-#include <stdlib.h>
-#include <string.h>
-#include <stdio.h>
-#include <sys/types.h>
-
-#include <CoreFoundation/CoreFoundation.h>
-#include <Security/SecureTransport.h>
-#include <Security/Security.h>
-
-#include "bin/builtin.h"
-#include "bin/dartutils.h"
-#include "bin/lockers.h"
-#include "bin/reference_counting.h"
-#include "bin/socket.h"
-#include "bin/thread.h"
-#include "bin/utils.h"
-
-namespace dart {
-namespace bin {
-
-// SSLCertContext wraps the certificates needed for a SecureTransport
-// connection. Fields are protected by the mutex_ field, and may only be set
-// once. This is to allow access by both the Dart thread and the IOService
-// thread. Setters return false if the field was already set.
-class SSLCertContext : public ReferenceCounted<SSLCertContext> {
- public:
- SSLCertContext()
- : ReferenceCounted(),
- mutex_(new Mutex()),
- private_key_(NULL),
- keychain_(NULL),
- cert_chain_(NULL),
- trusted_certs_(NULL),
- cert_authorities_(NULL),
- trust_builtin_(false) {}
-
- ~SSLCertContext() {
- {
- MutexLocker m(mutex_);
- if (private_key_ != NULL) {
- CFRelease(private_key_);
- }
- if (keychain_ != NULL) {
- SecKeychainDelete(keychain_);
- CFRelease(keychain_);
- }
- if (cert_chain_ != NULL) {
- CFRelease(cert_chain_);
- }
- if (trusted_certs_ != NULL) {
- CFRelease(trusted_certs_);
- }
- if (cert_authorities_ != NULL) {
- CFRelease(cert_authorities_);
- }
- }
- delete mutex_;
- }
-
- SecKeyRef private_key() {
- MutexLocker m(mutex_);
- return private_key_;
- }
- bool set_private_key(SecKeyRef private_key) {
- MutexLocker m(mutex_);
- if (private_key_ != NULL) {
- return false;
- }
- private_key_ = private_key;
- return true;
- }
-
- SecKeychainRef keychain() {
- MutexLocker m(mutex_);
- return keychain_;
- }
- bool set_keychain(SecKeychainRef keychain) {
- MutexLocker m(mutex_);
- if (keychain_ != NULL) {
- return false;
- }
- keychain_ = keychain;
- return true;
- }
-
- CFArrayRef cert_chain() {
- MutexLocker m(mutex_);
- return cert_chain_;
- }
- bool set_cert_chain(CFArrayRef cert_chain) {
- MutexLocker m(mutex_);
- if (cert_chain_ != NULL) {
- return false;
- }
- cert_chain_ = cert_chain;
- return true;
- }
-
- CFArrayRef trusted_certs() {
- MutexLocker m(mutex_);
- return trusted_certs_;
- }
- bool set_trusted_certs(CFArrayRef trusted_certs) {
- MutexLocker m(mutex_);
- if (trusted_certs_ != NULL) {
- return false;
- }
- trusted_certs_ = trusted_certs;
- return true;
- }
-
- CFArrayRef cert_authorities() {
- MutexLocker m(mutex_);
- return cert_authorities_;
- }
- bool set_cert_authorities(CFArrayRef cert_authorities) {
- MutexLocker m(mutex_);
- if (cert_authorities_ != NULL) {
- return false;
- }
- cert_authorities_ = cert_authorities;
- return true;
- }
-
- bool trust_builtin() {
- MutexLocker m(mutex_);
- return trust_builtin_;
- }
- void set_trust_builtin(bool trust_builtin) {
- MutexLocker m(mutex_);
- trust_builtin_ = trust_builtin;
- }
-
- private:
- // The context is accessed both by Dart code and the IOService. This mutex
- // protects all fields.
- Mutex* mutex_;
-
- SecKeyRef private_key_;
- SecKeychainRef keychain_;
-
- // CFArrays of SecCertificateRef.
- CFArrayRef cert_chain_;
- CFArrayRef trusted_certs_;
- CFArrayRef cert_authorities_;
-
- bool trust_builtin_;
-
- DISALLOW_COPY_AND_ASSIGN(SSLCertContext);
-};
-
-// SSLFilter encapsulates the SecureTransport code in a filter that communicates
-// with the containing _SecureFilterImpl Dart object through four shared
-// ExternalByteArray buffers, for reading and writing plaintext, and
-// reading and writing encrypted text. The filter handles handshaking
-// and certificate verification.
-class SSLFilter : public ReferenceCounted<SSLFilter> {
- public:
- // These enums must agree with those in sdk/lib/io/secure_socket.dart.
- enum BufferIndex {
- kReadPlaintext,
- kWritePlaintext,
- kReadEncrypted,
- kWriteEncrypted,
- kNumBuffers,
- kFirstEncrypted = kReadEncrypted
- };
-
- SSLFilter()
- : ReferenceCounted(),
- cert_context_(NULL),
- ssl_context_(NULL),
- peer_certs_(NULL),
- string_start_(NULL),
- string_length_(NULL),
- handshake_complete_(NULL),
- bad_certificate_callback_(NULL),
- in_handshake_(false),
- connected_(false),
- bad_cert_(false),
- is_server_(false),
- hostname_(NULL) {}
-
- ~SSLFilter();
-
- // Callback called by the IOService.
- static CObject* ProcessFilterRequest(const CObjectArray& request);
-
- Dart_Handle Init(Dart_Handle dart_this);
- void Connect(Dart_Handle dart_this,
- const char* hostname,
- SSLCertContext* context,
- bool is_server,
- bool request_client_certificate,
- bool require_client_certificate);
- void Destroy();
- OSStatus CheckHandshake();
- void Renegotiate(bool use_session_cache,
- bool request_client_certificate,
- bool require_client_certificate);
- void RegisterHandshakeCompleteCallback(Dart_Handle handshake_complete);
- void RegisterBadCertificateCallback(Dart_Handle callback);
- Dart_Handle PeerCertificate();
-
- private:
- static OSStatus SSLReadCallback(SSLConnectionRef connection,
- void* data,
- size_t* data_length);
- static OSStatus SSLWriteCallback(SSLConnectionRef connection,
- const void* data,
- size_t* data_length);
-
- static bool isBufferEncrypted(intptr_t i) {
- return static_cast<BufferIndex>(i) >= kFirstEncrypted;
- }
- Dart_Handle InitializeBuffers(Dart_Handle dart_this);
-
- intptr_t GetBufferStart(intptr_t idx) const;
- intptr_t GetBufferEnd(intptr_t idx) const;
- void SetBufferStart(intptr_t idx, intptr_t value);
- void SetBufferEnd(intptr_t idx, intptr_t value);
-
- OSStatus ProcessAllBuffers(intptr_t starts[kNumBuffers],
- intptr_t ends[kNumBuffers],
- bool in_handshake);
- OSStatus ProcessReadPlaintextBuffer(intptr_t start,
- intptr_t end,
- intptr_t* bytes_processed);
- OSStatus ProcessWritePlaintextBuffer(intptr_t start,
- intptr_t end,
- intptr_t* bytes_processed);
-
- // These calls can block on IO, and should only be invoked from
- // from ProcessAllBuffers from ProcessFilterRequest.
- OSStatus EvaluatePeerTrust();
- OSStatus Handshake();
- Dart_Handle InvokeBadCertCallback(SecCertificateRef peer_cert);
-
- RetainedPointer<SSLCertContext> cert_context_;
- SSLContextRef ssl_context_;
- CFArrayRef peer_certs_;
-
- // starts and ends filled in at the start of ProcessAllBuffers.
- // If these are NULL, then try to get the pointers out of
- // dart_buffer_objects_.
- uint8_t* buffers_[kNumBuffers];
- intptr_t* buffer_starts_[kNumBuffers];
- intptr_t* buffer_ends_[kNumBuffers];
- intptr_t buffer_size_;
- intptr_t encrypted_buffer_size_;
- Dart_PersistentHandle string_start_;
- Dart_PersistentHandle string_length_;
- Dart_PersistentHandle dart_buffer_objects_[kNumBuffers];
- Dart_PersistentHandle handshake_complete_;
- Dart_PersistentHandle bad_certificate_callback_;
- bool in_handshake_;
- bool connected_;
- bool bad_cert_;
- bool is_server_;
- char* hostname_;
-
- DISALLOW_COPY_AND_ASSIGN(SSLFilter);
-};
-
-} // namespace bin
-} // namespace dart
-
-#endif // RUNTIME_BIN_SECURE_SOCKET_MACOS_H_
diff --git a/runtime/bin/secure_socket_patch.dart b/runtime/bin/secure_socket_patch.dart
index 735ba3b..012aee9 100644
--- a/runtime/bin/secure_socket_patch.dart
+++ b/runtime/bin/secure_socket_patch.dart
@@ -121,8 +121,8 @@
@patch
class SecurityContext {
@patch
- factory SecurityContext() {
- return new _SecurityContext();
+ factory SecurityContext({bool withTrustedRoots: false}) {
+ return new _SecurityContext(withTrustedRoots);
}
@patch
@@ -138,14 +138,16 @@
class _SecurityContext extends NativeFieldWrapperClass1
implements SecurityContext {
- _SecurityContext() {
+ _SecurityContext(bool withTrustedRoots) {
_createNativeContext();
+ if (withTrustedRoots) {
+ _trustBuiltinRoots();
+ }
}
void _createNativeContext() native "SecurityContext_Allocate";
- static final SecurityContext defaultContext = new _SecurityContext()
- .._trustBuiltinRoots();
+ static final SecurityContext defaultContext = new _SecurityContext(true);
void usePrivateKey(String file, {String password}) {
List<int> bytes = (new File(file)).readAsBytesSync();
diff --git a/runtime/bin/secure_socket_utils.cc b/runtime/bin/secure_socket_utils.cc
new file mode 100644
index 0000000..4ed1a67
--- /dev/null
+++ b/runtime/bin/secure_socket_utils.cc
@@ -0,0 +1,99 @@
+// 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_utils.h"
+
+#include <openssl/err.h>
+#include <openssl/ssl.h>
+
+#include "platform/globals.h"
+
+#include "bin/file.h"
+#include "bin/log.h"
+#include "bin/secure_socket_filter.h"
+#include "bin/security_context.h"
+
+namespace dart {
+namespace bin {
+
+// Get the error messages from BoringSSL, and put them in buffer as a
+// null-terminated string.
+void SecureSocketUtils::FetchErrorString(const SSL* ssl,
+ TextBuffer* text_buffer) {
+ const char* sep = File::PathSeparator();
+ while (true) {
+ const char* path = NULL;
+ int line = -1;
+ uint32_t error = ERR_get_error_line(&path, &line);
+ if (error == 0) {
+ break;
+ }
+ text_buffer->Printf("\n\t%s", ERR_reason_error_string(error));
+ if ((ssl != NULL) && (ERR_GET_LIB(error) == ERR_LIB_SSL) &&
+ (ERR_GET_REASON(error) == SSL_R_CERTIFICATE_VERIFY_FAILED)) {
+ intptr_t result = SSL_get_verify_result(ssl);
+ text_buffer->Printf(": %s", X509_verify_cert_error_string(result));
+ }
+ if ((path != NULL) && (line >= 0)) {
+ const char* file = strrchr(path, sep[0]);
+ path = file ? file + 1 : path;
+ text_buffer->Printf("(%s:%d)", path, line);
+ }
+ }
+}
+
+
+// Handle an error reported from the BoringSSL library.
+void SecureSocketUtils::ThrowIOException(int status,
+ const char* exception_type,
+ const char* message,
+ const SSL* ssl) {
+ Dart_Handle exception;
+ {
+ TextBuffer error_string(SSL_ERROR_MESSAGE_BUFFER_SIZE);
+ SecureSocketUtils::FetchErrorString(ssl, &error_string);
+ OSError os_error_struct(status, error_string.buf(), OSError::kBoringSSL);
+ Dart_Handle os_error = DartUtils::NewDartOSError(&os_error_struct);
+ exception =
+ DartUtils::NewDartIOException(exception_type, message, os_error);
+ ASSERT(!Dart_IsError(exception));
+ }
+ Dart_ThrowException(exception);
+ UNREACHABLE();
+}
+
+
+void SecureSocketUtils::CheckStatusSSL(int status,
+ const char* type,
+ const char* message,
+ const SSL* ssl) {
+ // TODO(24183): Take appropriate action on failed calls,
+ // throw exception that includes all messages from the error stack.
+ if (status == 1) {
+ return;
+ }
+ if (SSL_LOG_STATUS) {
+ int error = ERR_get_error();
+ Log::PrintErr("Failed: %s status %d", message, status);
+ char error_string[SSL_ERROR_MESSAGE_BUFFER_SIZE];
+ ERR_error_string_n(error, error_string, SSL_ERROR_MESSAGE_BUFFER_SIZE);
+ Log::PrintErr("ERROR: %d %s\n", error, error_string);
+ }
+ SecureSocketUtils::ThrowIOException(status, type, message, ssl);
+}
+
+
+void SecureSocketUtils::CheckStatus(int status,
+ const char* type,
+ const char* message) {
+ SecureSocketUtils::CheckStatusSSL(status, type, message, NULL);
+}
+
+} // namespace bin
+} // namespace dart
+
+#endif // !defined(DART_IO_DISABLED) &&
+ // !defined(DART_IO_SECURE_SOCKET_DISABLED)
diff --git a/runtime/bin/secure_socket_utils.h b/runtime/bin/secure_socket_utils.h
new file mode 100644
index 0000000..50a8ad5
--- /dev/null
+++ b/runtime/bin/secure_socket_utils.h
@@ -0,0 +1,180 @@
+// 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.
+
+#ifndef RUNTIME_BIN_SECURE_SOCKET_UTILS_H_
+#define RUNTIME_BIN_SECURE_SOCKET_UTILS_H_
+
+#include <openssl/bio.h>
+#include <openssl/err.h>
+#include <openssl/pkcs12.h>
+#include <openssl/ssl.h>
+#include <openssl/x509.h>
+
+#include "platform/globals.h"
+
+#include "bin/dartutils.h"
+#include "platform/text_buffer.h"
+
+namespace dart {
+namespace bin {
+
+const bool SSL_LOG_STATUS = false;
+const bool SSL_LOG_DATA = false;
+const bool SSL_LOG_CERTS = false;
+
+class SecureSocketUtils : public AllStatic {
+ public:
+ static const int SSL_ERROR_MESSAGE_BUFFER_SIZE = 1000;
+
+ static void ThrowIOException(int status,
+ const char* exception_type,
+ const char* message,
+ const SSL* ssl);
+
+ static void CheckStatusSSL(int status,
+ const char* type,
+ const char* message,
+ const SSL* ssl);
+
+ static void CheckStatus(int status, const char* type, const char* message);
+
+ static bool NoPEMStartLine() {
+ uint32_t last_error = ERR_peek_last_error();
+ return (ERR_GET_LIB(last_error) == ERR_LIB_PEM) &&
+ (ERR_GET_REASON(last_error) == PEM_R_NO_START_LINE);
+ }
+
+ static void FetchErrorString(const SSL* ssl, TextBuffer* text_buffer);
+};
+
+
+// Where the argument to the constructor is the handle for an object
+// implementing List<int>, this class creates a scope in which a memory-backed
+// BIO is allocated. Leaving the scope cleans up the BIO and the buffer that
+// was used to create it.
+//
+// Do not make Dart_ API calls while in a ScopedMemBIO.
+// Do not call Dart_PropagateError while in a ScopedMemBIO.
+class ScopedMemBIO {
+ public:
+ explicit ScopedMemBIO(Dart_Handle object) {
+ if (!Dart_IsTypedData(object) && !Dart_IsList(object)) {
+ Dart_ThrowException(
+ DartUtils::NewDartArgumentError("Argument is not a List<int>"));
+ }
+
+ uint8_t* bytes = NULL;
+ intptr_t bytes_len = 0;
+ bool is_typed_data = false;
+ if (Dart_IsTypedData(object)) {
+ is_typed_data = true;
+ Dart_TypedData_Type typ;
+ ThrowIfError(Dart_TypedDataAcquireData(
+ object, &typ, reinterpret_cast<void**>(&bytes), &bytes_len));
+ } else {
+ ASSERT(Dart_IsList(object));
+ ThrowIfError(Dart_ListLength(object, &bytes_len));
+ bytes = Dart_ScopeAllocate(bytes_len);
+ ASSERT(bytes != NULL);
+ ThrowIfError(Dart_ListGetAsBytes(object, 0, bytes, bytes_len));
+ }
+
+ object_ = object;
+ bytes_ = bytes;
+ bytes_len_ = bytes_len;
+ bio_ = BIO_new_mem_buf(bytes, bytes_len);
+ ASSERT(bio_ != NULL);
+ is_typed_data_ = is_typed_data;
+ }
+
+ ~ScopedMemBIO() {
+ ASSERT(bio_ != NULL);
+ if (is_typed_data_) {
+ BIO_free(bio_);
+ ThrowIfError(Dart_TypedDataReleaseData(object_));
+ } else {
+ BIO_free(bio_);
+ }
+ }
+
+ BIO* bio() {
+ ASSERT(bio_ != NULL);
+ return bio_;
+ }
+
+ private:
+ Dart_Handle object_;
+ uint8_t* bytes_;
+ intptr_t bytes_len_;
+ BIO* bio_;
+ bool is_typed_data_;
+
+ DISALLOW_ALLOCATION();
+ DISALLOW_COPY_AND_ASSIGN(ScopedMemBIO);
+};
+
+template <typename T, void (*free_func)(T*)>
+class ScopedSSLType {
+ public:
+ explicit ScopedSSLType(T* obj) : obj_(obj) {}
+
+ ~ScopedSSLType() {
+ if (obj_ != NULL) {
+ free_func(obj_);
+ }
+ }
+
+ T* get() { return obj_; }
+ const T* get() const { return obj_; }
+
+ T* release() {
+ T* result = obj_;
+ obj_ = NULL;
+ return result;
+ }
+
+ private:
+ T* obj_;
+
+ DISALLOW_ALLOCATION();
+ DISALLOW_COPY_AND_ASSIGN(ScopedSSLType);
+};
+
+template <typename T, typename E, void (*func)(E*)>
+class ScopedSSLStackType {
+ public:
+ explicit ScopedSSLStackType(T* obj) : obj_(obj) {}
+
+ ~ScopedSSLStackType() {
+ if (obj_ != NULL) {
+ sk_pop_free(reinterpret_cast<_STACK*>(obj_),
+ reinterpret_cast<void (*)(void*)>(func));
+ }
+ }
+
+ T* get() { return obj_; }
+ const T* get() const { return obj_; }
+
+ T* release() {
+ T* result = obj_;
+ obj_ = NULL;
+ return result;
+ }
+
+ private:
+ T* obj_;
+
+ DISALLOW_ALLOCATION();
+ DISALLOW_COPY_AND_ASSIGN(ScopedSSLStackType);
+};
+
+
+typedef ScopedSSLType<PKCS12, PKCS12_free> ScopedPKCS12;
+typedef ScopedSSLType<X509, X509_free> ScopedX509;
+typedef ScopedSSLStackType<STACK_OF(X509), X509, X509_free> ScopedX509Stack;
+
+} // namespace bin
+} // namespace dart
+
+#endif // RUNTIME_BIN_SECURE_SOCKET_UTILS_H_
diff --git a/runtime/bin/security_context.cc b/runtime/bin/security_context.cc
new file mode 100644
index 0000000..f394c3b
--- /dev/null
+++ b/runtime/bin/security_context.cc
@@ -0,0 +1,866 @@
+// 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/security_context.h"
+
+#include <openssl/bio.h>
+#include <openssl/err.h>
+#include <openssl/pkcs12.h>
+#include <openssl/ssl.h>
+#include <openssl/x509.h>
+
+#include "platform/globals.h"
+
+#include "bin/directory.h"
+#include "bin/file.h"
+#include "bin/log.h"
+#include "bin/secure_socket_filter.h"
+#include "bin/secure_socket_utils.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 {
+
+int SSLCertContext::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] = X509Helper::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);
+}
+
+
+SSLCertContext* SSLCertContext::GetSecurityContext(Dart_NativeArguments args) {
+ SSLCertContext* context;
+ Dart_Handle dart_this = ThrowIfError(Dart_GetNativeArgument(args, 0));
+ ASSERT(Dart_IsInstance(dart_this));
+ ThrowIfError(Dart_GetNativeInstanceField(
+ dart_this, SSLCertContext::kSecurityContextNativeFieldIndex,
+ reinterpret_cast<intptr_t*>(&context)));
+ return context;
+}
+
+
+static void DeleteSecurityContext(void* isolate_data,
+ Dart_WeakPersistentHandle handle,
+ void* context_pointer) {
+ SSLCertContext* context = static_cast<SSLCertContext*>(context_pointer);
+ context->Release();
+}
+
+
+static Dart_Handle SetSecurityContext(Dart_NativeArguments args,
+ SSLCertContext* context) {
+ 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, SSLCertContext::kSecurityContextNativeFieldIndex,
+ reinterpret_cast<intptr_t>(context));
+ RETURN_IF_ERROR(err);
+ Dart_NewWeakPersistentHandle(dart_this, context,
+ SSLCertContext::kApproximateSize,
+ DeleteSecurityContext);
+ return Dart_Null();
+}
+
+
+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 X509Helper::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;
+}
+
+
+static int SetTrustedCertificatesBytesPKCS12(SSL_CTX* context,
+ BIO* bio,
+ const char* password) {
+ ScopedPKCS12 p12(d2i_PKCS12_bio(bio, NULL));
+ if (p12.get() == NULL) {
+ return 0;
+ }
+
+ EVP_PKEY* key = NULL;
+ X509* cert = NULL;
+ STACK_OF(X509)* ca_certs = NULL;
+ int status = PKCS12_parse(p12.get(), password, &key, &cert, &ca_certs);
+ if (status == 0) {
+ return status;
+ }
+
+ ScopedX509Stack cert_stack(ca_certs);
+ X509_STORE* store = SSL_CTX_get_cert_store(context);
+ status = X509_STORE_add_cert(store, cert);
+ // X509_STORE_add_cert increments the reference count of cert on success.
+ X509_free(cert);
+ if (status == 0) {
+ return status;
+ }
+
+ X509* ca;
+ while ((ca = sk_X509_shift(cert_stack.get())) != NULL) {
+ status = X509_STORE_add_cert(store, ca);
+ // X509_STORE_add_cert increments the reference count of cert on success.
+ X509_free(ca);
+ if (status == 0) {
+ return status;
+ }
+ }
+
+ return status;
+}
+
+
+static int SetTrustedCertificatesBytesPEM(SSL_CTX* context, BIO* bio) {
+ X509_STORE* store = SSL_CTX_get_cert_store(context);
+
+ int status = 0;
+ X509* cert = NULL;
+ while ((cert = PEM_read_bio_X509(bio, NULL, NULL, NULL)) != NULL) {
+ status = X509_STORE_add_cert(store, cert);
+ // X509_STORE_add_cert increments the reference count of cert on success.
+ X509_free(cert);
+ if (status == 0) {
+ return status;
+ }
+ }
+
+ // If no PEM start line is found, it means that we read to the end of the
+ // file, or that the file isn't PEM. In the first case, status will be
+ // non-zero indicating success. In the second case, status will be 0,
+ // indicating that we should try to read as PKCS12. If there is some other
+ // error, we return it up to the caller.
+ return SecureSocketUtils::NoPEMStartLine() ? status : 0;
+}
+
+
+void SSLCertContext::SetTrustedCertificatesBytes(Dart_Handle cert_bytes,
+ const char* password) {
+ int status = 0;
+ {
+ ScopedMemBIO bio(cert_bytes);
+ status = SetTrustedCertificatesBytesPEM(context(), bio.bio());
+ if (status == 0) {
+ if (SecureSocketUtils::NoPEMStartLine()) {
+ ERR_clear_error();
+ BIO_reset(bio.bio());
+ status =
+ SetTrustedCertificatesBytesPKCS12(context(), bio.bio(), password);
+ }
+ } else {
+ // The PEM file was successfully parsed.
+ ERR_clear_error();
+ }
+ }
+ SecureSocketUtils::CheckStatus(status, "TlsException",
+ "Failure trusting builtin roots");
+}
+
+
+static int SetClientAuthoritiesPKCS12(SSL_CTX* context,
+ BIO* bio,
+ const char* password) {
+ ScopedPKCS12 p12(d2i_PKCS12_bio(bio, NULL));
+ if (p12.get() == NULL) {
+ return 0;
+ }
+
+ EVP_PKEY* key = NULL;
+ X509* cert = NULL;
+ STACK_OF(X509)* ca_certs = NULL;
+ int status = PKCS12_parse(p12.get(), password, &key, &cert, &ca_certs);
+ if (status == 0) {
+ return status;
+ }
+
+ ScopedX509Stack cert_stack(ca_certs);
+ status = SSL_CTX_add_client_CA(context, cert);
+ // SSL_CTX_add_client_CA increments the reference count of cert on success.
+ X509_free(cert);
+ if (status == 0) {
+ return status;
+ }
+
+ X509* ca;
+ while ((ca = sk_X509_shift(cert_stack.get())) != NULL) {
+ status = SSL_CTX_add_client_CA(context, ca);
+ // SSL_CTX_add_client_CA increments the reference count of ca on success.
+ X509_free(ca); // The name has been extracted.
+ if (status == 0) {
+ return status;
+ }
+ }
+
+ return status;
+}
+
+
+static int SetClientAuthoritiesPEM(SSL_CTX* context, BIO* bio) {
+ int status = 0;
+ X509* cert = NULL;
+ while ((cert = PEM_read_bio_X509(bio, NULL, NULL, NULL)) != NULL) {
+ status = SSL_CTX_add_client_CA(context, cert);
+ X509_free(cert); // The name has been extracted.
+ if (status == 0) {
+ return status;
+ }
+ }
+ return SecureSocketUtils::NoPEMStartLine() ? status : 0;
+}
+
+
+static int SetClientAuthorities(SSL_CTX* context,
+ BIO* bio,
+ const char* password) {
+ int status = SetClientAuthoritiesPEM(context, bio);
+ if (status == 0) {
+ if (SecureSocketUtils::NoPEMStartLine()) {
+ ERR_clear_error();
+ BIO_reset(bio);
+ status = SetClientAuthoritiesPKCS12(context, bio, password);
+ }
+ } else {
+ // The PEM file was successfully parsed.
+ ERR_clear_error();
+ }
+ return status;
+}
+
+
+void SSLCertContext::SetClientAuthoritiesBytes(
+ Dart_Handle client_authorities_bytes,
+ const char* password) {
+ int status;
+ {
+ ScopedMemBIO bio(client_authorities_bytes);
+ status = SetClientAuthorities(context(), bio.bio(), password);
+ }
+
+ SecureSocketUtils::CheckStatus(status, "TlsException",
+ "Failure in setClientAuthoritiesBytes");
+}
+
+void SSLCertContext::LoadRootCertFile(const char* file) {
+ if (SSL_LOG_STATUS) {
+ Log::Print("Looking for trusted roots in %s\n", file);
+ }
+ if (!File::Exists(file)) {
+ SecureSocketUtils::ThrowIOException(-1, "TlsException",
+ "Failed to find root cert file", NULL);
+ }
+ int status = SSL_CTX_load_verify_locations(context(), file, NULL);
+ SecureSocketUtils::CheckStatus(status, "TlsException",
+ "Failure trusting builtin roots");
+ if (SSL_LOG_STATUS) {
+ Log::Print("Trusting roots from: %s\n", file);
+ }
+}
+
+
+void SSLCertContext::AddCompiledInCerts() {
+ if (root_certificates_pem == NULL) {
+ if (SSL_LOG_STATUS) {
+ Log::Print("Missing compiled-in roots\n");
+ }
+ return;
+ }
+ X509_STORE* store = SSL_CTX_get_cert_store(context());
+ BIO* roots_bio =
+ BIO_new_mem_buf(const_cast<unsigned char*>(root_certificates_pem),
+ root_certificates_pem_length);
+ X509* root_cert;
+ // PEM_read_bio_X509 reads PEM-encoded certificates from a bio (in our case,
+ // backed by a memory buffer), and returns X509 objects, one by one.
+ // When the end of the bio is reached, it returns null.
+ while ((root_cert = PEM_read_bio_X509(roots_bio, NULL, NULL, NULL)) != NULL) {
+ int status = X509_STORE_add_cert(store, root_cert);
+ // X509_STORE_add_cert increments the reference count of cert on success.
+ X509_free(root_cert);
+ if (status == 0) {
+ break;
+ }
+ }
+ BIO_free(roots_bio);
+ // If there is an error here, it must be the error indicating that we are done
+ // reading PEM certificates.
+ ASSERT((ERR_peek_error() == 0) || SecureSocketUtils::NoPEMStartLine());
+ ERR_clear_error();
+}
+
+
+void SSLCertContext::LoadRootCertCache(const char* cache) {
+ if (SSL_LOG_STATUS) {
+ Log::Print("Looking for trusted roots in %s\n", cache);
+ }
+ if (Directory::Exists(cache) != Directory::EXISTS) {
+ SecureSocketUtils::ThrowIOException(-1, "TlsException",
+ "Failed to find root cert cache", NULL);
+ }
+ int status = SSL_CTX_load_verify_locations(context(), NULL, cache);
+ SecureSocketUtils::CheckStatus(status, "TlsException",
+ "Failure trusting builtin roots");
+ if (SSL_LOG_STATUS) {
+ Log::Print("Trusting roots from: %s\n", cache);
+ }
+}
+
+
+int PasswordCallback(char* buf, int size, int rwflag, void* userdata) {
+ char* password = static_cast<char*>(userdata);
+ ASSERT(size == PEM_BUFSIZE);
+ strncpy(buf, password, size);
+ return strlen(password);
+}
+
+
+static EVP_PKEY* GetPrivateKeyPKCS12(BIO* bio, const char* password) {
+ ScopedPKCS12 p12(d2i_PKCS12_bio(bio, NULL));
+ if (p12.get() == NULL) {
+ return NULL;
+ }
+
+ EVP_PKEY* key = NULL;
+ X509* cert = NULL;
+ STACK_OF(X509)* ca_certs = NULL;
+ int status = PKCS12_parse(p12.get(), password, &key, &cert, &ca_certs);
+ if (status == 0) {
+ return NULL;
+ }
+
+ // We only care about the private key.
+ ScopedX509 delete_cert(cert);
+ ScopedX509Stack delete_ca_certs(ca_certs);
+ return key;
+}
+
+
+static EVP_PKEY* GetPrivateKey(BIO* bio, const char* password) {
+ EVP_PKEY* key = PEM_read_bio_PrivateKey(bio, NULL, PasswordCallback,
+ const_cast<char*>(password));
+ if (key == NULL) {
+ // We try reading data as PKCS12 only if reading as PEM was unsuccessful and
+ // if there is no indication that the data is malformed PEM. We assume the
+ // data is malformed PEM if it contains the start line, i.e. a line
+ // with ----- BEGIN.
+ if (SecureSocketUtils::NoPEMStartLine()) {
+ // Reset the bio, and clear the error from trying to read as PEM.
+ ERR_clear_error();
+ BIO_reset(bio);
+
+ // Try to decode as PKCS12.
+ key = GetPrivateKeyPKCS12(bio, password);
+ }
+ }
+ return key;
+}
+
+
+const char* SSLCertContext::GetPasswordArgument(Dart_NativeArguments args,
+ intptr_t index) {
+ Dart_Handle password_object =
+ ThrowIfError(Dart_GetNativeArgument(args, index));
+ const char* password = NULL;
+ if (Dart_IsString(password_object)) {
+ ThrowIfError(Dart_StringToCString(password_object, &password));
+ if (strlen(password) > PEM_BUFSIZE - 1) {
+ Dart_ThrowException(DartUtils::NewDartArgumentError(
+ "Password length is greater than 1023 (PEM_BUFSIZE)"));
+ }
+ } else if (Dart_IsNull(password_object)) {
+ password = "";
+ } else {
+ Dart_ThrowException(
+ DartUtils::NewDartArgumentError("Password is not a String or null"));
+ }
+ return password;
+}
+
+
+int AlpnCallback(SSL* ssl,
+ const uint8_t** out,
+ uint8_t* outlen,
+ const uint8_t* in,
+ unsigned int inlen,
+ void* arg) {
+ // 'in' and 'arg' are sequences of (length, data) strings with 1-byte lengths.
+ // 'arg' is 0-terminated. Finds the first string in 'arg' that is in 'in'.
+ uint8_t* server_list = static_cast<uint8_t*>(arg);
+ while (*server_list != 0) {
+ uint8_t protocol_length = *server_list++;
+ const uint8_t* client_list = in;
+ while (client_list < in + inlen) {
+ uint8_t client_protocol_length = *client_list++;
+ if (client_protocol_length == protocol_length) {
+ if (0 == memcmp(server_list, client_list, protocol_length)) {
+ *out = client_list;
+ *outlen = client_protocol_length;
+ return SSL_TLSEXT_ERR_OK; // Success
+ }
+ }
+ client_list += client_protocol_length;
+ }
+ server_list += protocol_length;
+ }
+ // TODO(23580): Make failure send a fatal alert instead of ignoring ALPN.
+ return SSL_TLSEXT_ERR_NOACK;
+}
+
+
+// Sets the protocol list for ALPN on a SSL object or a context.
+void SSLCertContext::SetAlpnProtocolList(Dart_Handle protocols_handle,
+ SSL* ssl,
+ SSLCertContext* context,
+ bool is_server) {
+ // Enable ALPN (application layer protocol negotiation) if the caller provides
+ // a valid list of supported protocols.
+ Dart_TypedData_Type protocols_type;
+ uint8_t* protocol_string = NULL;
+ uint8_t* protocol_string_copy = NULL;
+ intptr_t protocol_string_len = 0;
+ int status;
+
+ Dart_Handle result = Dart_TypedDataAcquireData(
+ protocols_handle, &protocols_type,
+ reinterpret_cast<void**>(&protocol_string), &protocol_string_len);
+ if (Dart_IsError(result)) {
+ Dart_PropagateError(result);
+ }
+
+ if (protocols_type != Dart_TypedData_kUint8) {
+ Dart_TypedDataReleaseData(protocols_handle);
+ Dart_PropagateError(Dart_NewApiError(
+ "Unexpected type for protocols (expected valid Uint8List)."));
+ }
+
+ if (protocol_string_len > 0) {
+ if (is_server) {
+ // ALPN on server connections must be set on an SSL_CTX object,
+ // not on the SSL object of the individual connection.
+ ASSERT(context != NULL);
+ ASSERT(ssl == NULL);
+ // Because it must be passed as a single void*, terminate
+ // the list of (length, data) strings with a length 0 string.
+ protocol_string_copy =
+ static_cast<uint8_t*>(malloc(protocol_string_len + 1));
+ memmove(protocol_string_copy, protocol_string, protocol_string_len);
+ protocol_string_copy[protocol_string_len] = '\0';
+ SSL_CTX_set_alpn_select_cb(context->context(), AlpnCallback,
+ protocol_string_copy);
+ context->set_alpn_protocol_string(protocol_string_copy);
+ } else {
+ // The function makes a local copy of protocol_string, which it owns.
+ if (ssl != NULL) {
+ ASSERT(context == NULL);
+ status = SSL_set_alpn_protos(ssl, protocol_string, protocol_string_len);
+ } else {
+ ASSERT(context != NULL);
+ ASSERT(ssl == NULL);
+ status = SSL_CTX_set_alpn_protos(context->context(), protocol_string,
+ protocol_string_len);
+ }
+ ASSERT(status == 0); // The function returns a non-standard status.
+ }
+ }
+ Dart_TypedDataReleaseData(protocols_handle);
+}
+
+
+static int UseChainBytesPKCS12(SSL_CTX* context,
+ BIO* bio,
+ const char* password) {
+ ScopedPKCS12 p12(d2i_PKCS12_bio(bio, NULL));
+ if (p12.get() == NULL) {
+ return 0;
+ }
+
+ EVP_PKEY* key = NULL;
+ X509* cert = NULL;
+ STACK_OF(X509)* ca_certs = NULL;
+ int status = PKCS12_parse(p12.get(), password, &key, &cert, &ca_certs);
+ if (status == 0) {
+ return status;
+ }
+
+ ScopedX509 x509(cert);
+ ScopedX509Stack certs(ca_certs);
+ status = SSL_CTX_use_certificate(context, x509.get());
+ if (ERR_peek_error() != 0) {
+ // Key/certificate mismatch doesn't imply status is 0.
+ status = 0;
+ }
+ if (status == 0) {
+ return status;
+ }
+
+ SSL_CTX_clear_chain_certs(context);
+
+ X509* ca;
+ while ((ca = sk_X509_shift(certs.get())) != NULL) {
+ status = SSL_CTX_add0_chain_cert(context, ca);
+ // SSL_CTX_add0_chain_cert does not inc ref count, so don't free unless the
+ // call fails.
+ if (status == 0) {
+ X509_free(ca);
+ return status;
+ }
+ }
+
+ return status;
+}
+
+
+static int UseChainBytesPEM(SSL_CTX* context, BIO* bio) {
+ int status = 0;
+ ScopedX509 x509(PEM_read_bio_X509_AUX(bio, NULL, NULL, NULL));
+ if (x509.get() == NULL) {
+ return 0;
+ }
+
+ status = SSL_CTX_use_certificate(context, x509.get());
+ if (ERR_peek_error() != 0) {
+ // Key/certificate mismatch doesn't imply status is 0.
+ status = 0;
+ }
+ if (status == 0) {
+ return status;
+ }
+
+ SSL_CTX_clear_chain_certs(context);
+
+ X509* ca;
+ while ((ca = PEM_read_bio_X509(bio, NULL, NULL, NULL)) != NULL) {
+ status = SSL_CTX_add0_chain_cert(context, ca);
+ // SSL_CTX_add0_chain_cert does not inc ref count, so don't free unless the
+ // call fails.
+ if (status == 0) {
+ X509_free(ca);
+ return status;
+ }
+ // Note that we must not free `ca` if it was successfully added to the
+ // chain. We must free the main certificate x509, though since its reference
+ // count is increased by SSL_CTX_use_certificate.
+ }
+
+ return SecureSocketUtils::NoPEMStartLine() ? status : 0;
+}
+
+
+static int UseChainBytes(SSL_CTX* context, BIO* bio, const char* password) {
+ int status = UseChainBytesPEM(context, bio);
+ if (status == 0) {
+ if (SecureSocketUtils::NoPEMStartLine()) {
+ ERR_clear_error();
+ BIO_reset(bio);
+ status = UseChainBytesPKCS12(context, bio, password);
+ }
+ } else {
+ // The PEM file was successfully read.
+ ERR_clear_error();
+ }
+ return status;
+}
+
+
+int SSLCertContext::UseCertificateChainBytes(Dart_Handle cert_chain_bytes,
+ const char* password) {
+ ScopedMemBIO bio(cert_chain_bytes);
+ return UseChainBytes(context(), bio.bio(), password);
+}
+
+
+static X509* GetX509Certificate(Dart_NativeArguments args) {
+ X509* certificate = NULL;
+ Dart_Handle dart_this = ThrowIfError(Dart_GetNativeArgument(args, 0));
+ ASSERT(Dart_IsInstance(dart_this));
+ ThrowIfError(Dart_GetNativeInstanceField(
+ dart_this, SSLCertContext::kX509NativeFieldIndex,
+ reinterpret_cast<intptr_t*>(&certificate)));
+ return certificate;
+}
+
+
+Dart_Handle X509Helper::GetSubject(Dart_NativeArguments args) {
+ X509* certificate = GetX509Certificate(args);
+ X509_NAME* subject = X509_get_subject_name(certificate);
+ char* subject_string = X509_NAME_oneline(subject, NULL, 0);
+ if (subject_string == NULL) {
+ Dart_ThrowException(DartUtils::NewDartArgumentError(
+ "X509.subject failed to find subject's common name."));
+ }
+ Dart_Handle subject_handle = Dart_NewStringFromCString(subject_string);
+ OPENSSL_free(subject_string);
+ return subject_handle;
+}
+
+
+Dart_Handle X509Helper::GetIssuer(Dart_NativeArguments args) {
+ fprintf(stdout, "Getting issuer!\n");
+ X509* certificate = GetX509Certificate(args);
+ X509_NAME* issuer = X509_get_issuer_name(certificate);
+ char* issuer_string = X509_NAME_oneline(issuer, NULL, 0);
+ if (issuer_string == NULL) {
+ Dart_ThrowException(DartUtils::NewDartArgumentError(
+ "X509.issuer failed to find issuer's common name."));
+ }
+ Dart_Handle issuer_handle = Dart_NewStringFromCString(issuer_string);
+ OPENSSL_free(issuer_string);
+ return issuer_handle;
+}
+
+
+static Dart_Handle ASN1TimeToMilliseconds(ASN1_TIME* aTime) {
+ ASN1_UTCTIME* epoch_start = M_ASN1_UTCTIME_new();
+ ASN1_UTCTIME_set_string(epoch_start, "700101000000Z");
+ int days;
+ int seconds;
+ int result = ASN1_TIME_diff(&days, &seconds, epoch_start, aTime);
+ M_ASN1_UTCTIME_free(epoch_start);
+ if (result != 1) {
+ // TODO(whesse): Propagate an error to Dart.
+ Log::PrintErr("ASN1Time error %d\n", result);
+ }
+ return Dart_NewInteger((86400LL * days + seconds) * 1000LL);
+}
+
+
+Dart_Handle X509Helper::GetStartValidity(Dart_NativeArguments args) {
+ X509* certificate = GetX509Certificate(args);
+ ASN1_TIME* not_before = X509_get_notBefore(certificate);
+ return ASN1TimeToMilliseconds(not_before);
+}
+
+
+Dart_Handle X509Helper::GetEndValidity(Dart_NativeArguments args) {
+ X509* certificate = GetX509Certificate(args);
+ ASN1_TIME* not_after = X509_get_notAfter(certificate);
+ return ASN1TimeToMilliseconds(not_after);
+}
+
+void FUNCTION_NAME(SecurityContext_UsePrivateKeyBytes)(
+ Dart_NativeArguments args) {
+ SSLCertContext* context = SSLCertContext::GetSecurityContext(args);
+ const char* password = SSLCertContext::GetPasswordArgument(args, 2);
+
+ int status;
+ {
+ ScopedMemBIO bio(ThrowIfError(Dart_GetNativeArgument(args, 1)));
+ EVP_PKEY* key = GetPrivateKey(bio.bio(), password);
+ status = SSL_CTX_use_PrivateKey(context->context(), key);
+ // SSL_CTX_use_PrivateKey increments the reference count of key on success,
+ // so we have to call EVP_PKEY_free on both success and failure.
+ EVP_PKEY_free(key);
+ }
+
+ // TODO(24184): Handle different expected errors here - file missing,
+ // incorrect password, file not a PEM, and throw exceptions.
+ // SecureSocketUtils::CheckStatus should also throw an exception in uncaught
+ // cases.
+ SecureSocketUtils::CheckStatus(status, "TlsException",
+ "Failure in usePrivateKeyBytes");
+}
+
+
+void FUNCTION_NAME(SecurityContext_Allocate)(Dart_NativeArguments args) {
+ SSLFilter::InitializeLibrary();
+ SSL_CTX* ctx = SSL_CTX_new(TLS_method());
+ SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, SSLCertContext::CertificateCallback);
+ SSL_CTX_set_min_proto_version(ctx, TLS1_VERSION);
+ SSL_CTX_set_cipher_list(ctx, "HIGH:MEDIUM");
+ SSLCertContext* context = new SSLCertContext(ctx);
+ Dart_Handle err = SetSecurityContext(args, context);
+ if (Dart_IsError(err)) {
+ delete context;
+ Dart_PropagateError(err);
+ }
+}
+
+
+void FUNCTION_NAME(SecurityContext_SetTrustedCertificatesBytes)(
+ Dart_NativeArguments args) {
+ SSLCertContext* context = SSLCertContext::GetSecurityContext(args);
+ Dart_Handle cert_bytes = ThrowIfError(Dart_GetNativeArgument(args, 1));
+ const char* password = SSLCertContext::GetPasswordArgument(args, 2);
+
+ ASSERT(context != NULL);
+ ASSERT(password != NULL);
+ context->SetTrustedCertificatesBytes(cert_bytes, password);
+}
+
+
+void FUNCTION_NAME(SecurityContext_SetClientAuthoritiesBytes)(
+ Dart_NativeArguments args) {
+ SSLCertContext* context = SSLCertContext::GetSecurityContext(args);
+ Dart_Handle client_authorities_bytes =
+ ThrowIfError(Dart_GetNativeArgument(args, 1));
+ const char* password = SSLCertContext::GetPasswordArgument(args, 2);
+
+ ASSERT(context != NULL);
+ ASSERT(password != NULL);
+
+ context->SetClientAuthoritiesBytes(client_authorities_bytes, password);
+}
+
+
+void FUNCTION_NAME(SecurityContext_UseCertificateChainBytes)(
+ Dart_NativeArguments args) {
+ SSLCertContext* context = SSLCertContext::GetSecurityContext(args);
+ Dart_Handle cert_chain_bytes = ThrowIfError(Dart_GetNativeArgument(args, 1));
+ const char* password = SSLCertContext::GetPasswordArgument(args, 2);
+
+ ASSERT(context != NULL);
+ ASSERT(password != NULL);
+
+ int status = context->UseCertificateChainBytes(cert_chain_bytes, password);
+
+ SecureSocketUtils::CheckStatus(status, "TlsException",
+ "Failure in useCertificateChainBytes");
+}
+
+
+void FUNCTION_NAME(SecurityContext_AlpnSupported)(Dart_NativeArguments args) {
+ Dart_SetReturnValue(args, Dart_NewBoolean(true));
+}
+
+
+void FUNCTION_NAME(SecurityContext_TrustBuiltinRoots)(
+ Dart_NativeArguments args) {
+ SSLCertContext* context = SSLCertContext::GetSecurityContext(args);
+
+ ASSERT(context != NULL);
+
+ context->TrustBuiltinRoots();
+}
+
+
+void FUNCTION_NAME(X509_Subject)(Dart_NativeArguments args) {
+ Dart_SetReturnValue(args, X509Helper::GetSubject(args));
+}
+
+
+void FUNCTION_NAME(X509_Issuer)(Dart_NativeArguments args) {
+ Dart_SetReturnValue(args, X509Helper::GetIssuer(args));
+}
+
+
+void FUNCTION_NAME(X509_StartValidity)(Dart_NativeArguments args) {
+ Dart_SetReturnValue(args, X509Helper::GetStartValidity(args));
+}
+
+
+void FUNCTION_NAME(X509_EndValidity)(Dart_NativeArguments args) {
+ Dart_SetReturnValue(args, X509Helper::GetEndValidity(args));
+}
+
+
+void FUNCTION_NAME(SecurityContext_SetAlpnProtocols)(
+ Dart_NativeArguments args) {
+ SSLCertContext* context = SSLCertContext::GetSecurityContext(args);
+ Dart_Handle protocols_handle = ThrowIfError(Dart_GetNativeArgument(args, 1));
+ Dart_Handle is_server_handle = ThrowIfError(Dart_GetNativeArgument(args, 2));
+ if (Dart_IsBoolean(is_server_handle)) {
+ bool is_server = DartUtils::GetBooleanValue(is_server_handle);
+ SSLCertContext::SetAlpnProtocolList(protocols_handle, NULL, context,
+ is_server);
+ } else {
+ Dart_ThrowException(DartUtils::NewDartArgumentError(
+ "Non-boolean is_server argument passed to SetAlpnProtocols"));
+ }
+}
+
+} // namespace bin
+} // namespace dart
+
+#endif // !defined(DART_IO_DISABLED) &&
+ // !defined(DART_IO_SECURE_SOCKET_DISABLED)
diff --git a/runtime/bin/security_context.h b/runtime/bin/security_context.h
new file mode 100644
index 0000000..eaba5a5
--- /dev/null
+++ b/runtime/bin/security_context.h
@@ -0,0 +1,104 @@
+// 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.
+
+#ifndef RUNTIME_BIN_SECURITY_CONTEXT_H_
+#define RUNTIME_BIN_SECURITY_CONTEXT_H_
+
+#include <openssl/ssl.h>
+#include <openssl/x509.h>
+
+#include "bin/lockers.h"
+#include "bin/reference_counting.h"
+#include "bin/socket.h"
+
+namespace dart {
+namespace bin {
+
+// Forward declaration
+class SSLFilter;
+
+class SSLCertContext : public ReferenceCounted<SSLCertContext> {
+ public:
+ static const intptr_t kApproximateSize;
+ static const int kSecurityContextNativeFieldIndex = 0;
+ static const int kX509NativeFieldIndex = 0;
+
+ explicit SSLCertContext(SSL_CTX* context)
+ : ReferenceCounted(),
+ context_(context),
+ alpn_protocol_string_(NULL),
+ trust_builtin_(false) {}
+
+ ~SSLCertContext() {
+ SSL_CTX_free(context_);
+ if (alpn_protocol_string_ != NULL) {
+ free(alpn_protocol_string_);
+ }
+ }
+
+ static int CertificateCallback(int preverify_ok, X509_STORE_CTX* store_ctx);
+
+ static SSLCertContext* GetSecurityContext(Dart_NativeArguments args);
+ static const char* GetPasswordArgument(Dart_NativeArguments args,
+ intptr_t index);
+ static void SetAlpnProtocolList(Dart_Handle protocols_handle,
+ SSL* ssl,
+ SSLCertContext* context,
+ bool is_server);
+
+ void SetTrustedCertificatesBytes(Dart_Handle cert_bytes,
+ const char* password);
+
+ void SetClientAuthoritiesBytes(Dart_Handle client_authorities_bytes,
+ const char* password);
+
+ int UseCertificateChainBytes(Dart_Handle cert_chain_bytes,
+ const char* password);
+
+ void TrustBuiltinRoots();
+
+ SSL_CTX* context() const { return context_; }
+
+ uint8_t* alpn_protocol_string() const { return alpn_protocol_string_; }
+
+ void set_alpn_protocol_string(uint8_t* protocol_string) {
+ if (alpn_protocol_string_ != NULL) {
+ free(alpn_protocol_string_);
+ }
+ alpn_protocol_string_ = protocol_string;
+ }
+
+ bool trust_builtin() const { return trust_builtin_; }
+
+ void set_trust_builtin(bool trust_builtin) { trust_builtin_ = trust_builtin; }
+
+ void RegisterCallbacks(SSL* ssl);
+
+ private:
+ void AddCompiledInCerts();
+ void LoadRootCertFile(const char* file);
+ void LoadRootCertCache(const char* cache);
+
+ SSL_CTX* context_;
+ uint8_t* alpn_protocol_string_;
+
+ bool trust_builtin_;
+
+ DISALLOW_COPY_AND_ASSIGN(SSLCertContext);
+};
+
+
+class X509Helper : public AllStatic {
+ public:
+ static Dart_Handle GetSubject(Dart_NativeArguments args);
+ static Dart_Handle GetIssuer(Dart_NativeArguments args);
+ static Dart_Handle GetStartValidity(Dart_NativeArguments args);
+ static Dart_Handle GetEndValidity(Dart_NativeArguments args);
+ static Dart_Handle WrappedX509Certificate(X509* certificate);
+};
+
+} // namespace bin
+} // namespace dart
+
+#endif // RUNTIME_BIN_SECURITY_CONTEXT_H_
diff --git a/runtime/bin/security_context_android.cc b/runtime/bin/security_context_android.cc
new file mode 100644
index 0000000..dc4eaf8
--- /dev/null
+++ b/runtime/bin/security_context_android.cc
@@ -0,0 +1,71 @@
+// 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 "platform/globals.h"
+#if defined(HOST_OS_ANDROID)
+
+#include "bin/security_context.h"
+
+#include <openssl/bio.h>
+#include <openssl/ssl.h>
+#include <openssl/x509.h>
+
+#include "bin/directory.h"
+#include "bin/file.h"
+#include "bin/log.h"
+#include "bin/secure_socket_filter.h"
+#include "bin/secure_socket_utils.h"
+
+namespace dart {
+namespace bin {
+
+// The security context won't necessarily use the compiled-in root certificates,
+// but since there is no way to update the size of the allocation after creating
+// the weak persistent handle, we assume that it will. Note that when the
+// root certs aren't compiled in, |root_certificates_pem_length| is 0.
+const intptr_t SSLCertContext::kApproximateSize =
+ sizeof(SSLCertContext) + root_certificates_pem_length;
+
+const char* commandline_root_certs_file = NULL;
+const char* commandline_root_certs_cache = NULL;
+
+void SSLCertContext::TrustBuiltinRoots() {
+ // First, try to use locations specified on the command line.
+ if (commandline_root_certs_file != NULL) {
+ LoadRootCertFile(commandline_root_certs_file);
+ return;
+ }
+
+ if (commandline_root_certs_cache != NULL) {
+ LoadRootCertCache(commandline_root_certs_cache);
+ return;
+ }
+
+ // On Android, we don't compile in the trusted root certificates. Insead,
+ // we use the directory of trusted certificates already present on the device.
+ // This saves ~240KB from the size of the binary. This has the drawback that
+ // SSL_do_handshake will synchronously hit the filesystem looking for root
+ // certs during its trust evaluation. We call SSL_do_handshake directly from
+ // the Dart thread so that Dart code can be invoked from the "bad certificate"
+ // callback called by SSL_do_handshake.
+ const char* android_cacerts = "/system/etc/security/cacerts";
+ LoadRootCertCache(android_cacerts);
+ return;
+}
+
+
+void SSLCertContext::RegisterCallbacks(SSL* ssl) {
+ // No callbacks to register for implementations using BoringSSL's built-in
+ // verification mechanism.
+}
+
+} // namespace bin
+} // namespace dart
+
+#endif // defined(HOST_OS_ANDROID)
+
+#endif // !defined(DART_IO_DISABLED) &&
+ // !defined(DART_IO_SECURE_SOCKET_DISABLED)
diff --git a/runtime/bin/security_context_fuchsia.cc b/runtime/bin/security_context_fuchsia.cc
new file mode 100644
index 0000000..c49476b
--- /dev/null
+++ b/runtime/bin/security_context_fuchsia.cc
@@ -0,0 +1,67 @@
+// 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 "platform/globals.h"
+#if defined(HOST_OS_FUCHSIA)
+
+#include "bin/security_context.h"
+
+#include <openssl/bio.h>
+#include <openssl/ssl.h>
+#include <openssl/x509.h>
+
+#include "bin/directory.h"
+#include "bin/file.h"
+#include "bin/log.h"
+#include "bin/secure_socket_filter.h"
+#include "bin/secure_socket_utils.h"
+
+namespace dart {
+namespace bin {
+
+// The security context won't necessarily use the compiled-in root certificates,
+// but since there is no way to update the size of the allocation after creating
+// the weak persistent handle, we assume that it will. Note that when the
+// root certs aren't compiled in, |root_certificates_pem_length| is 0.
+const intptr_t SSLCertContext::kApproximateSize =
+ sizeof(SSLCertContext) + root_certificates_pem_length;
+
+const char* commandline_root_certs_file = NULL;
+const char* commandline_root_certs_cache = NULL;
+
+void SSLCertContext::TrustBuiltinRoots() {
+ // First, try to use locations specified on the command line.
+ if (commandline_root_certs_file != NULL) {
+ LoadRootCertFile(commandline_root_certs_file);
+ return;
+ }
+
+ if (commandline_root_certs_cache != NULL) {
+ LoadRootCertCache(commandline_root_certs_cache);
+ return;
+ }
+
+ // Fall back on the compiled-in certs if the standard locations don't exist,
+ // or we aren't on Linux.
+ if (SSL_LOG_STATUS) {
+ Log::Print("Trusting compiled-in roots\n");
+ }
+ AddCompiledInCerts();
+}
+
+
+void SSLCertContext::RegisterCallbacks(SSL* ssl) {
+ // No callbacks to register for implementations using BoringSSL's built-in
+ // verification mechanism.
+}
+
+} // namespace bin
+} // namespace dart
+
+#endif // defined(HOST_OS_FUCHSIA)
+
+#endif // !defined(DART_IO_DISABLED) &&
+ // !defined(DART_IO_SECURE_SOCKET_DISABLED)
diff --git a/runtime/bin/security_context_linux.cc b/runtime/bin/security_context_linux.cc
new file mode 100644
index 0000000..2985fff
--- /dev/null
+++ b/runtime/bin/security_context_linux.cc
@@ -0,0 +1,84 @@
+// 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 "platform/globals.h"
+#if defined(HOST_OS_LINUX)
+
+#include "bin/security_context.h"
+
+#include <openssl/bio.h>
+#include <openssl/ssl.h>
+#include <openssl/x509.h>
+
+#include "bin/directory.h"
+#include "bin/file.h"
+#include "bin/log.h"
+#include "bin/secure_socket_filter.h"
+#include "bin/secure_socket_utils.h"
+
+namespace dart {
+namespace bin {
+
+// The security context won't necessarily use the compiled-in root certificates,
+// but since there is no way to update the size of the allocation after creating
+// the weak persistent handle, we assume that it will. Note that when the
+// root certs aren't compiled in, |root_certificates_pem_length| is 0.
+const intptr_t SSLCertContext::kApproximateSize =
+ sizeof(SSLCertContext) + root_certificates_pem_length;
+
+const char* commandline_root_certs_file = NULL;
+const char* commandline_root_certs_cache = NULL;
+
+void SSLCertContext::TrustBuiltinRoots() {
+ // First, try to use locations specified on the command line.
+ if (commandline_root_certs_file != NULL) {
+ LoadRootCertFile(commandline_root_certs_file);
+ return;
+ }
+
+ if (commandline_root_certs_cache != NULL) {
+ LoadRootCertCache(commandline_root_certs_cache);
+ return;
+ }
+
+ // On Linux, we use the compiled-in trusted certs as a last resort. First,
+ // we try to find the trusted certs in various standard locations. A good
+ // discussion of the complexities of this endeavor can be found here:
+ //
+ // https://www.happyassassin.net/2015/01/12/a-note-about-ssltls-trusted-certificate-stores-and-platforms/
+ const char* bundle = "/etc/pki/tls/certs/ca-bundle.crt";
+ const char* cachedir = "/etc/ssl/certs";
+ if (File::Exists(bundle)) {
+ LoadRootCertFile(bundle);
+ return;
+ }
+
+ if (Directory::Exists(cachedir) == Directory::EXISTS) {
+ LoadRootCertCache(cachedir);
+ return;
+ }
+
+ // Fall back on the compiled-in certs if the standard locations don't exist,
+ // or we aren't on Linux.
+ if (SSL_LOG_STATUS) {
+ Log::Print("Trusting compiled-in roots\n");
+ }
+ AddCompiledInCerts();
+}
+
+
+void SSLCertContext::RegisterCallbacks(SSL* ssl) {
+ // No callbacks to register for implementations using BoringSSL's built-in
+ // verification mechanism.
+}
+
+} // namespace bin
+} // namespace dart
+
+#endif // defined(HOST_OS_LINUX)
+
+#endif // !defined(DART_IO_DISABLED) &&
+ // !defined(DART_IO_SECURE_SOCKET_DISABLED)
diff --git a/runtime/bin/security_context_macos.cc b/runtime/bin/security_context_macos.cc
new file mode 100644
index 0000000..3ae88f7
--- /dev/null
+++ b/runtime/bin/security_context_macos.cc
@@ -0,0 +1,191 @@
+// 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 "platform/globals.h"
+#if defined(HOST_OS_MACOS)
+
+#include "bin/security_context.h"
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <Security/SecureTransport.h>
+#include <Security/Security.h>
+
+#include <openssl/ssl.h>
+#include <openssl/x509.h>
+
+#include "bin/secure_socket_filter.h"
+
+namespace dart {
+namespace bin {
+
+const intptr_t SSLCertContext::kApproximateSize = sizeof(SSLCertContext);
+
+const char* commandline_root_certs_file = NULL;
+const char* commandline_root_certs_cache = NULL;
+
+template <typename T>
+class ScopedCFType {
+ public:
+ explicit ScopedCFType(T obj) : obj_(obj) {}
+
+ ~ScopedCFType() { CFRelease(obj_); }
+
+ T get() { return obj_; }
+ T* ptr() { return &obj_; }
+ const T get() const { return obj_; }
+
+ void set(T obj) { obj_ = obj; }
+
+ private:
+ T obj_;
+
+ DISALLOW_ALLOCATION();
+ DISALLOW_COPY_AND_ASSIGN(ScopedCFType);
+};
+
+typedef ScopedCFType<CFMutableArrayRef> ScopedCFMutableArrayRef;
+typedef ScopedCFType<CFDataRef> ScopedCFDataRef;
+typedef ScopedCFType<SecPolicyRef> ScopedSecPolicyRef;
+typedef ScopedCFType<SecCertificateRef> ScopedSecCertificateRef;
+typedef ScopedCFType<SecTrustRef> ScopedSecTrustRef;
+
+static SecCertificateRef CreateSecCertificateFromX509(X509* cert) {
+ if (cert == NULL) {
+ return NULL;
+ }
+ unsigned char* deb_cert = NULL;
+ int length = i2d_X509(cert, &deb_cert);
+ if (length < 0) {
+ return 0;
+ }
+ ASSERT(deb_cert != NULL);
+ ScopedCFDataRef cert_buf(
+ CFDataCreateWithBytesNoCopy(NULL, deb_cert, length, kCFAllocatorNull));
+ SecCertificateRef auth_cert =
+ SecCertificateCreateWithData(NULL, cert_buf.get());
+ if (auth_cert == NULL) {
+ return NULL;
+ }
+ return auth_cert;
+}
+
+
+static int CertificateVerificationCallback(X509_STORE_CTX* ctx, void* arg) {
+ SSLCertContext* context = static_cast<SSLCertContext*>(arg);
+
+ // Convert BoringSSL formatted certificates to SecCertificate certificates.
+ ScopedCFMutableArrayRef cert_chain(NULL);
+ X509* root_cert = NULL;
+ if (ctx->untrusted != NULL) {
+ STACK_OF(X509)* user_provided_certs = ctx->untrusted;
+ int num_certs = sk_X509_num(user_provided_certs);
+ int current_cert = 0;
+ cert_chain.set(CFArrayCreateMutable(NULL, num_certs, NULL));
+ X509* ca;
+ while ((ca = sk_X509_shift(user_provided_certs)) != NULL) {
+ SecCertificateRef cert = CreateSecCertificateFromX509(ca);
+ if (cert == NULL) {
+ return ctx->verify_cb(0, ctx);
+ }
+ CFArrayAppendValue(cert_chain.get(), cert);
+ ++current_cert;
+
+ if (current_cert == num_certs) {
+ root_cert = ca;
+ }
+ }
+ }
+
+ // Convert all trusted certificates provided by the user via
+ // setTrustedCertificatesBytes or the command line into SecCertificates.
+ ScopedCFMutableArrayRef trusted_certs(CFArrayCreateMutable(NULL, 0, NULL));
+ X509_STORE* store = ctx->ctx;
+ ASSERT(store != NULL);
+
+ if (store->objs != NULL) {
+ for (uintptr_t i = 0; i < sk_X509_OBJECT_num(store->objs); ++i) {
+ X509* ca = sk_X509_OBJECT_value(store->objs, i)->data.x509;
+ SecCertificateRef cert = CreateSecCertificateFromX509(ca);
+ if (cert == NULL) {
+ return ctx->verify_cb(0, ctx);
+ }
+ CFArrayAppendValue(trusted_certs.get(), cert);
+ }
+ }
+
+ // Generate a generic X509 verification policy.
+ ScopedSecPolicyRef policy(SecPolicyCreateBasicX509());
+
+ // Create the trust object with the certificates provided by the user.
+ ScopedSecTrustRef trust(NULL);
+ OSStatus status = SecTrustCreateWithCertificates(cert_chain.get(),
+ policy.get(), trust.ptr());
+ if (status != noErr) {
+ return ctx->verify_cb(0, ctx);
+ }
+
+ // If the user provided any additional CA certificates, add them to the trust
+ // object.
+ if (CFArrayGetCount(trusted_certs.get()) > 0) {
+ status = SecTrustSetAnchorCertificates(trust.get(), trusted_certs.get());
+ if (status != noErr) {
+ return ctx->verify_cb(0, ctx);
+ }
+ }
+
+ // Specify whether or not to use the built-in CA certificates for
+ // verification.
+ status =
+ SecTrustSetAnchorCertificatesOnly(trust.get(), !context->trust_builtin());
+ if (status != noErr) {
+ return ctx->verify_cb(0, ctx);
+ }
+
+ // Perform the certificate verification.
+ SecTrustResultType trust_result;
+ status = SecTrustEvaluate(trust.get(), &trust_result);
+ if (status != noErr) {
+ return ctx->verify_cb(0, ctx);
+ }
+
+ if ((trust_result == kSecTrustResultProceed) ||
+ (trust_result == kSecTrustResultUnspecified)) {
+ // Successfully verified certificate!
+ return ctx->verify_cb(1, ctx);
+ }
+
+ // Set current_cert to the root of the certificate chain. This will be passed
+ // to the callback provided by the user for additional verification steps.
+ ctx->current_cert = root_cert;
+ return ctx->verify_cb(0, ctx);
+}
+
+
+void SSLCertContext::RegisterCallbacks(SSL* ssl) {
+ SSL_CTX* ctx = SSL_get_SSL_CTX(ssl);
+ SSL_CTX_set_cert_verify_callback(ctx, CertificateVerificationCallback, this);
+}
+
+
+void SSLCertContext::TrustBuiltinRoots() {
+ // First, try to use locations specified on the command line.
+ if (commandline_root_certs_file != NULL) {
+ LoadRootCertFile(commandline_root_certs_file);
+ return;
+ }
+ if (commandline_root_certs_cache != NULL) {
+ LoadRootCertCache(commandline_root_certs_cache);
+ return;
+ }
+ set_trust_builtin(true);
+}
+
+} // namespace bin
+} // namespace dart
+
+#endif // defined(HOST_OS_MACOS)
+#endif // !defined(DART_IO_DISABLED) &&
+ // !defined(DART_IO_SECURE_SOCKET_DISABLED)
diff --git a/runtime/bin/security_context_win.cc b/runtime/bin/security_context_win.cc
new file mode 100644
index 0000000..2c228c3
--- /dev/null
+++ b/runtime/bin/security_context_win.cc
@@ -0,0 +1,67 @@
+// 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 "platform/globals.h"
+#if defined(HOST_OS_WINDOWS)
+
+#include "bin/security_context.h"
+
+#include <openssl/bio.h>
+#include <openssl/ssl.h>
+#include <openssl/x509.h>
+
+#include "bin/directory.h"
+#include "bin/file.h"
+#include "bin/log.h"
+#include "bin/secure_socket_filter.h"
+#include "bin/secure_socket_utils.h"
+
+namespace dart {
+namespace bin {
+
+// The security context won't necessarily use the compiled-in root certificates,
+// but since there is no way to update the size of the allocation after creating
+// the weak persistent handle, we assume that it will. Note that when the
+// root certs aren't compiled in, |root_certificates_pem_length| is 0.
+const intptr_t SSLCertContext::kApproximateSize =
+ sizeof(SSLCertContext) + root_certificates_pem_length;
+
+const char* commandline_root_certs_file = NULL;
+const char* commandline_root_certs_cache = NULL;
+
+void SSLCertContext::TrustBuiltinRoots() {
+ // First, try to use locations specified on the command line.
+ if (commandline_root_certs_file != NULL) {
+ LoadRootCertFile(commandline_root_certs_file);
+ return;
+ }
+
+ if (commandline_root_certs_cache != NULL) {
+ LoadRootCertCache(commandline_root_certs_cache);
+ return;
+ }
+
+ // Fall back on the compiled-in certs if the standard locations don't exist,
+ // or we aren't on Linux.
+ if (SSL_LOG_STATUS) {
+ Log::Print("Trusting compiled-in roots\n");
+ }
+ AddCompiledInCerts();
+}
+
+
+void SSLCertContext::RegisterCallbacks(SSL* ssl) {
+ // No callbacks to register for implementations using BoringSSL's built-in
+ // verification mechanism.
+}
+
+} // namespace bin
+} // namespace dart
+
+#endif // defined(HOST_OS_WINDOWS)
+
+#endif // !defined(DART_IO_DISABLED) &&
+ // !defined(DART_IO_SECURE_SOCKET_DISABLED)
diff --git a/sdk/lib/_internal/js_runtime/lib/io_patch.dart b/sdk/lib/_internal/js_runtime/lib/io_patch.dart
index 59aa7ff..688cf4a 100644
--- a/sdk/lib/_internal/js_runtime/lib/io_patch.dart
+++ b/sdk/lib/_internal/js_runtime/lib/io_patch.dart
@@ -451,7 +451,7 @@
@patch
class SecurityContext {
@patch
- factory SecurityContext() {
+ factory SecurityContext({bool withTrustedRoots: false}) {
throw new UnsupportedError("SecurityContext constructor");
}
diff --git a/sdk/lib/io/security_context.dart b/sdk/lib/io/security_context.dart
index 8d55c19..7169370 100644
--- a/sdk/lib/io/security_context.dart
+++ b/sdk/lib/io/security_context.dart
@@ -20,7 +20,21 @@
* be used, by way of [SecurityContext.defaultContext].
*/
abstract class SecurityContext {
- external factory SecurityContext();
+ /**
+ * Creates a new [SecurityContext].
+ *
+ * By default, the created [SecurityContext] contains no keys or certificates.
+ * These can be added by calling the methods of this class.
+ *
+ * If `withTrustedRoots` is passed as `true`, the [SecurityContext] will be
+ * seeded by the trusted root certificates provided as explained below. To
+ * obtain a [SecurityContext] containing trusted root certificates,
+ * [SecurityContext.defaultContext] is usually sufficient, and should
+ * be used instead. However, if the [SecurityContext] containing the trusted
+ * root certificates must be modified per-connection, then `withTrustedRoots`
+ * should be used.
+ */
+ external factory SecurityContext({bool withTrustedRoots: false});
/**
* Secure networking classes with an optional `context` parameter
diff --git a/tests/standalone/io/secure_builtin_roots_test.dart b/tests/standalone/io/secure_builtin_roots_test.dart
index e1c5321..2b1ddee 100644
--- a/tests/standalone/io/secure_builtin_roots_test.dart
+++ b/tests/standalone/io/secure_builtin_roots_test.dart
@@ -35,6 +35,8 @@
asyncStart();
await testGoogleUrl(null, "pass");
await testGoogleUrl(SecurityContext.defaultContext, "pass");
+ await testGoogleUrl(new SecurityContext(withTrustedRoots: true), "pass");
+ await testGoogleUrl(new SecurityContext(withTrustedRoots: false), "fail");
await testGoogleUrl(new SecurityContext(), "fail");
asyncEnd();
}
diff --git a/tools/VERSION b/tools/VERSION
index 123dfeb..7be09ef 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -26,6 +26,6 @@
CHANNEL stable
MAJOR 1
MINOR 24
-PATCH 2
+PATCH 3
PRERELEASE 0
PRERELEASE_PATCH 0