| Index: runtime/bin/secure_socket_ios.cc
|
| diff --git a/runtime/bin/secure_socket_ios.cc b/runtime/bin/secure_socket_ios.cc
|
| deleted file mode 100644
|
| index e2d9abc73054818171f31f44c50d6ae0d2f0f4f1..0000000000000000000000000000000000000000
|
| --- 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)
|
|
|