mirror of
https://github.com/dart-lang/sdk
synced 2024-09-19 23:51:47 +00:00
3126ade6d0
BUG=US-109 R=asiva@google.com, zra@google.com Review URL: https://codereview.chromium.org/2545833003 .
1783 lines
58 KiB
C++
1783 lines
58 KiB
C++
// 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(TARGET_OS_ANDROID) || defined(TARGET_OS_LINUX) || \
|
|
defined(TARGET_OS_WINDOWS) || defined(TARGET_OS_FUCHSIA)
|
|
|
|
#include "bin/secure_socket.h"
|
|
#include "bin/secure_socket_boringssl.h"
|
|
|
|
#include <errno.h>
|
|
#include <fcntl.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/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(char* buffer, int length) {
|
|
buffer[0] = '\0';
|
|
int error = ERR_get_error();
|
|
while (error != 0) {
|
|
int used = strlen(buffer);
|
|
int free_length = length - used;
|
|
if (free_length > 16) {
|
|
// Enough room for error code at least.
|
|
if (used > 0) {
|
|
buffer[used] = '\n';
|
|
buffer[used + 1] = '\0';
|
|
used++;
|
|
free_length--;
|
|
}
|
|
ERR_error_string_n(error, buffer + used, free_length);
|
|
// ERR_error_string_n is guaranteed to leave a null-terminated string.
|
|
}
|
|
error = ERR_get_error();
|
|
}
|
|
}
|
|
|
|
|
|
/* Handle an error reported from the BoringSSL library. */
|
|
static void ThrowIOException(int status,
|
|
const char* exception_type,
|
|
const char* message) {
|
|
char error_string[SSL_ERROR_MESSAGE_BUFFER_SIZE];
|
|
FetchErrorString(error_string, SSL_ERROR_MESSAGE_BUFFER_SIZE);
|
|
Dart_Handle exception;
|
|
{
|
|
OSError os_error_struct(status, error_string, 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);
|
|
// 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) {
|
|
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_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 CheckStatus(int status, const char* type, const char* message) {
|
|
// 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);
|
|
}
|
|
|
|
|
|
// 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");
|
|
}
|
|
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");
|
|
}
|
|
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(TARGET_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(TARGET_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(TARGET_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());
|
|
char error_string[SSL_ERROR_MESSAGE_BUFFER_SIZE];
|
|
FetchErrorString(error_string, SSL_ERROR_MESSAGE_BUFFER_SIZE);
|
|
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)));
|
|
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);
|
|
CheckStatus(status, "TlsException", "BIO_new_bio_pair");
|
|
|
|
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);
|
|
CheckStatus(status, "TlsException", "Set SNI host name");
|
|
// 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_));
|
|
CheckStatus(status, "TlsException",
|
|
"Set hostname for certificate checking");
|
|
}
|
|
// 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;
|
|
}
|
|
CheckStatus(status, "HandshakeException", is_server_
|
|
? "Handshake error in server"
|
|
: "Handshake error in client");
|
|
// 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.
|
|
}
|
|
|
|
|
|
SSLFilter::~SSLFilter() {
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
|
|
/* 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(TARGET_OS_LINUX)
|
|
|
|
#endif // !defined(DART_IO_DISABLED) &&
|
|
// !defined(DART_IO_SECURE_SOCKET_DISABLED)
|