Index: runtime/bin/secure_socket.cc |
diff --git a/runtime/bin/secure_socket.cc b/runtime/bin/secure_socket.cc |
index 63f600ff7915027afb7974ae80279568d4db5b03..8f27ef93a69ae79a2585c05168ccc78abebd221d 100644 |
--- a/runtime/bin/secure_socket.cc |
+++ b/runtime/bin/secure_socket.cc |
@@ -10,21 +10,17 @@ |
#include <stdio.h> |
#include <string.h> |
-#include <key.h> |
-#include <keyt.h> |
-#include <nss.h> |
-#include <pk11pub.h> |
-#include <prerror.h> |
-#include <prinit.h> |
-#include <prnetdb.h> |
-#include <secmod.h> |
-#include <ssl.h> |
-#include <sslproto.h> |
+#include <openssl/bio.h> |
+#include <openssl/err.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/lockers.h" |
-#include "bin/net/nss_memio.h" |
+#include "bin/log.h" |
#include "bin/socket.h" |
#include "bin/thread.h" |
#include "bin/utils.h" |
@@ -32,27 +28,33 @@ |
#include "include/dart_api.h" |
- |
namespace dart { |
namespace bin { |
bool SSLFilter::library_initialized_ = false; |
// To protect library initialization. |
Mutex* SSLFilter::mutex_ = new Mutex(); |
-// The password is needed when creating secure server sockets. It can |
-// be null if only secure client sockets are used. |
-const char* SSLFilter::password_ = NULL; |
+int SSLFilter::filter_ssl_index; |
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 = 200; |
-/* Handle an error reported from the NSS library. */ |
-static void ThrowPRException(const char* exception_type, |
+/* Handle an error reported from the BoringSSL library. */ |
+static void ThrowIOException(const char* exception_type, |
const char* message, |
bool free_message = false) { |
- PRErrorCode error_code = PR_GetError(); |
- const char* error_message = PR_ErrorToString(error_code, PR_LANGUAGE_EN); |
- OSError os_error_struct(error_code, error_message, OSError::kNSS); |
+ // TODO(24068): Get the error code and message from the error stack. |
+ // There may be more than one error on the stack - should we |
+ // concatenate the error messages? |
+ int error_code = 0; |
+ const char* error_message = "Unknown error from BoringSSL library"; |
+ OSError os_error_struct(error_code, error_message, OSError::kBoringSSL); |
Dart_Handle os_error = DartUtils::NewDartOSError(&os_error_struct); |
Dart_Handle exception = |
DartUtils::NewDartIOException(exception_type, message, os_error); |
@@ -64,20 +66,6 @@ static void ThrowPRException(const char* exception_type, |
} |
-static void ThrowCertificateException(const char* format, |
- const char* certificate_name) { |
- int length = strlen(certificate_name); |
- length += strlen(format); |
- char* message = reinterpret_cast<char*>(malloc(length + 1)); |
- if (message == NULL) { |
- FATAL("Out of memory formatting CertificateException for throwing"); |
- } |
- snprintf(message, length + 1, format, certificate_name); |
- message[length] = '\0'; |
- ThrowPRException("CertificateException", message, true); |
-} |
- |
- |
static SSLFilter* GetFilter(Dart_NativeArguments args) { |
SSLFilter* filter; |
Dart_Handle dart_this = ThrowIfError(Dart_GetNativeArgument(args, 0)); |
@@ -100,6 +88,48 @@ static void SetFilter(Dart_NativeArguments args, SSLFilter* filter) { |
} |
+static SSL_CTX* GetSecurityContext(Dart_NativeArguments args) { |
+ SSL_CTX* 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 SetSecurityContext(Dart_NativeArguments args, |
+ SSL_CTX* context) { |
+ Dart_Handle dart_this = ThrowIfError(Dart_GetNativeArgument(args, 0)); |
+ ASSERT(Dart_IsInstance(dart_this)); |
+ ThrowIfError(Dart_SetNativeInstanceField( |
+ dart_this, |
+ kSecurityContextNativeFieldIndex, |
+ reinterpret_cast<intptr_t>(context))); |
+} |
+ |
+ |
+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, |
+ SSL_CTX* 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; |
@@ -110,50 +140,36 @@ void FUNCTION_NAME(SecureSocket_Init)(Dart_NativeArguments args) { |
void FUNCTION_NAME(SecureSocket_Connect)(Dart_NativeArguments args) { |
Dart_Handle host_name_object = ThrowIfError(Dart_GetNativeArgument(args, 1)); |
- Dart_Handle host_sockaddr_storage_object = |
- ThrowIfError(Dart_GetNativeArgument(args, 2)); |
- Dart_Handle port_object = ThrowIfError(Dart_GetNativeArgument(args, 3)); |
- bool is_server = DartUtils::GetBooleanValue(Dart_GetNativeArgument(args, 4)); |
- Dart_Handle certificate_name_object = |
- ThrowIfError(Dart_GetNativeArgument(args, 5)); |
+ 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, 6)); |
+ DartUtils::GetBooleanValue(Dart_GetNativeArgument(args, 4)); |
bool require_client_certificate = |
- DartUtils::GetBooleanValue(Dart_GetNativeArgument(args, 7)); |
+ DartUtils::GetBooleanValue(Dart_GetNativeArgument(args, 5)); |
bool send_client_certificate = |
- DartUtils::GetBooleanValue(Dart_GetNativeArgument(args, 8)); |
+ DartUtils::GetBooleanValue(Dart_GetNativeArgument(args, 6)); |
Dart_Handle protocols_handle = |
- ThrowIfError(Dart_GetNativeArgument(args, 9)); |
+ ThrowIfError(Dart_GetNativeArgument(args, 7)); |
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)); |
- RawAddr raw_addr; |
- SocketAddress::GetSockAddr(host_sockaddr_storage_object, &raw_addr); |
- |
- int64_t port; |
- if (!DartUtils::GetInt64Value(port_object, &port)) { |
- FATAL("The range of port_object was checked in Dart - it cannot fail here"); |
- } |
- |
- const char* certificate_name = NULL; |
- if (Dart_IsString(certificate_name_object)) { |
- ThrowIfError(Dart_StringToCString(certificate_name_object, |
- &certificate_name)); |
+ SSL_CTX* context = NULL; |
+ if (!Dart_IsNull(context_object)) { |
+ ThrowIfError(Dart_GetNativeInstanceField( |
+ context_object, |
+ kSecurityContextNativeFieldIndex, |
+ reinterpret_cast<intptr_t*>(&context))); |
} |
- // If this is a server connection, it must have a certificate to connect with. |
- ASSERT(!is_server || certificate_name != NULL); |
// 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, |
- raw_addr, |
- static_cast<int>(port), |
+ context, |
is_server, |
- certificate_name, |
request_client_certificate, |
require_client_certificate, |
send_client_certificate, |
@@ -217,59 +233,300 @@ void FUNCTION_NAME(SecureSocket_RegisterBadCertificateCallback)( |
} |
-void FUNCTION_NAME(SecureSocket_InitializeLibrary) |
+void FUNCTION_NAME(SecureSocket_PeerCertificate) |
(Dart_NativeArguments args) { |
- Dart_Handle certificate_database_object = |
- ThrowIfError(Dart_GetNativeArgument(args, 0)); |
- // Check that the type is string, and get the UTF-8 C string value from it. |
- const char* certificate_database = NULL; |
- if (Dart_IsString(certificate_database_object)) { |
- ThrowIfError(Dart_StringToCString(certificate_database_object, |
- &certificate_database)); |
- } else if (!Dart_IsNull(certificate_database_object)) { |
- Dart_ThrowException(DartUtils::NewDartArgumentError( |
- "Non-String certificate directory argument to SetCertificateDatabase")); |
+ Dart_SetReturnValue(args, GetFilter(args)->PeerCertificate()); |
+} |
+ |
+ |
+void FUNCTION_NAME(SecureSocket_FilterPointer)(Dart_NativeArguments args) { |
+ intptr_t filter_pointer = reinterpret_cast<intptr_t>(GetFilter(args)); |
+ Dart_SetReturnValue(args, Dart_NewInteger(filter_pointer)); |
+} |
+ |
+ |
+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)) { |
+ 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)); |
+ Dart_Handle status = Dart_SetNativeInstanceField( |
+ result, |
+ kX509NativeFieldIndex, |
+ reinterpret_cast<intptr_t>(certificate)); |
+ if (Dart_IsError(status)) { |
+ return status; |
+ } |
+ 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; |
+ 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* context = SSL_CTX_new(TLS_method()); |
+ SSL_CTX_set_verify(context, SSL_VERIFY_PEER, CertificateCallback); |
+ SSL_CTX_set_min_version(context, TLS1_VERSION); |
+ SSL_CTX_set_cipher_list(context, "HIGH:MEDIUM"); |
+ SSL_CTX_set_cipher_list_tls11(context, "HIGH:MEDIUM"); |
+ SetSecurityContext(args, context); |
+ // TODO(whesse): Use WeakPersistentHandle to free the SSL_CTX |
+ // when the object is GC'd. Also free the alpn_select_cb data pointer, |
+ // if non-null (allocated in SetAlpnProtocolList). |
+} |
+ |
+ |
+int PasswordCallback(char* buf, int size, int rwflag, void* userdata) { |
+ char* password = static_cast<char*>(userdata); |
+ if (static_cast<size_t>(size) < strlen(password) + 1) { |
+ Log::PrintErr("Password buffer too small.\n"); |
+ exit(1); |
+ // TODO(24182): Find the actual value of size passed in here, and |
+ // check for password length longer than this in the Dart function |
+ // that passes in the password, so we never have this problem. |
+ } |
+ strncpy(buf, static_cast<char*>(userdata), size); |
+ return strlen(static_cast<char*>(userdata)); |
+} |
+ |
+ |
+void CheckStatus(int status, const char* message, int line) { |
+ // TODO(24183): Take appropriate action on failed calls, |
+ // throw exception that includes all messages from the error stack. |
+ if (status != 1 && SSL_LOG_STATUS) { |
+ int error = ERR_get_error(); |
+ Log::PrintErr("Failed: %s line %d\n", message, line); |
+ 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); |
} |
- // Leave certificate_database as NULL if no value was provided. |
+} |
+ |
- Dart_Handle password_object = ThrowIfError(Dart_GetNativeArgument(args, 1)); |
- // Check that the type is string or null, |
- // and get the UTF-8 C string value from it. |
+void FUNCTION_NAME(SecurityContext_UsePrivateKey)(Dart_NativeArguments args) { |
+ SSL_CTX* context = GetSecurityContext(args); |
+ Dart_Handle filename_object = ThrowIfError(Dart_GetNativeArgument(args, 1)); |
+ const char* filename = NULL; |
+ if (Dart_IsString(filename_object)) { |
+ ThrowIfError(Dart_StringToCString(filename_object, &filename)); |
+ } else { |
+ Dart_ThrowException(DartUtils::NewDartArgumentError( |
+ "File argument to SecurityContext.usePrivateKey is not a String")); |
+ } |
+ Dart_Handle password_object = ThrowIfError(Dart_GetNativeArgument(args, 2)); |
const char* password = NULL; |
if (Dart_IsString(password_object)) { |
ThrowIfError(Dart_StringToCString(password_object, &password)); |
} else if (Dart_IsNull(password_object)) { |
- // Pass the empty string as the password. |
password = ""; |
} else { |
Dart_ThrowException(DartUtils::NewDartArgumentError( |
- "Password argument to SetCertificateDatabase is not a String or null")); |
+ "Password argument to SecurityContext.usePrivateKey is not " |
+ "a String or null")); |
+ } |
+ |
+ SSL_CTX_set_default_passwd_cb(context, PasswordCallback); |
+ SSL_CTX_set_default_passwd_cb_userdata(context, const_cast<char*>(password)); |
+ int status = SSL_CTX_use_PrivateKey_file(context, |
+ filename, |
+ SSL_FILETYPE_PEM); |
+ // 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, "SSL_CTX_use_PrivateKey_file", __LINE__); |
+ SSL_CTX_set_default_passwd_cb_userdata(context, NULL); |
+} |
+ |
+ |
+void FUNCTION_NAME(SecurityContext_SetTrustedCertificates)( |
+ Dart_NativeArguments args) { |
+ SSL_CTX* context = GetSecurityContext(args); |
+ Dart_Handle filename_object = ThrowIfError(Dart_GetNativeArgument(args, 1)); |
+ const char* filename = NULL; |
+ if (Dart_IsString(filename_object)) { |
+ ThrowIfError(Dart_StringToCString(filename_object, &filename)); |
+ } |
+ Dart_Handle directory_object = ThrowIfError(Dart_GetNativeArgument(args, 2)); |
+ const char* directory = NULL; |
+ if (Dart_IsString(directory_object)) { |
+ ThrowIfError(Dart_StringToCString(directory_object, &directory)); |
+ } else if (Dart_IsNull(directory_object)) { |
+ directory = NULL; |
+ } else { |
+ Dart_ThrowException(DartUtils::NewDartArgumentError( |
+ "Directory argument to SecurityContext.usePrivateKey is not " |
+ "a String or null")); |
+ } |
+ |
+ int status = SSL_CTX_load_verify_locations(context, filename, directory); |
+ CheckStatus(status, "SSL_CTX_load_verify_locations", __LINE__); |
+} |
+ |
+ |
+void FUNCTION_NAME(SecurityContext_TrustBuiltinRoots)( |
+ Dart_NativeArguments args) { |
+ SSL_CTX* context = GetSecurityContext(args); |
+ X509_STORE* store = SSL_CTX_get_cert_store(context); |
+ BIO* roots_bio = |
+ BIO_new_mem_buf(const_cast<unsigned char*>(root_certificates_pem), |
+ root_certificates_pem_length); |
+ X509* root_cert; |
+ // PEM_read_bio_X509 reads PEM-encoded certificates from a bio (in our case, |
+ // backed by a memory buffer), and returns X509 objects, one by one. |
+ // When the end of the bio is reached, it returns null. |
+ while ((root_cert = PEM_read_bio_X509(roots_bio, NULL, NULL, NULL))) { |
+ X509_STORE_add_cert(store, root_cert); |
+ } |
+ BIO_free(roots_bio); |
+} |
+ |
+ |
+void FUNCTION_NAME(SecurityContext_UseCertificateChain)( |
+ Dart_NativeArguments args) { |
+ SSL_CTX* context = GetSecurityContext(args); |
+ Dart_Handle filename_object = ThrowIfError(Dart_GetNativeArgument(args, 1)); |
+ const char* filename = NULL; |
+ if (Dart_IsString(filename_object)) { |
+ ThrowIfError(Dart_StringToCString(filename_object, &filename)); |
+ } else { |
+ Dart_ThrowException(DartUtils::NewDartArgumentError( |
+ "file argument in SecurityContext.useCertificateChain" |
+ " is not a String")); |
+ } |
+ int status = SSL_CTX_use_certificate_chain_file(context, filename); |
+ CheckStatus(status, "SSL_CTX_use_certificate_chain_file", __LINE__); |
+} |
+ |
+ |
+void FUNCTION_NAME(SecurityContext_SetClientAuthorities)( |
+ Dart_NativeArguments args) { |
+ SSL_CTX* context = GetSecurityContext(args); |
+ Dart_Handle filename_object = ThrowIfError(Dart_GetNativeArgument(args, 1)); |
+ const char* filename = NULL; |
+ if (Dart_IsString(filename_object)) { |
+ ThrowIfError(Dart_StringToCString(filename_object, &filename)); |
+ } else { |
+ Dart_ThrowException(DartUtils::NewDartArgumentError( |
+ "file argument in SecurityContext.setClientAuthorities" |
+ " is not a String")); |
} |
+ STACK_OF(X509_NAME)* certificate_names; |
+ certificate_names = SSL_load_client_CA_file(filename); |
+ if (certificate_names != NULL) { |
+ SSL_CTX_set_client_CA_list(context, certificate_names); |
+ } else { |
+ Dart_ThrowException(DartUtils::NewDartArgumentError( |
+ "Could not load certificate names from file in SetClientAuthorities")); |
+ } |
+} |
- Dart_Handle builtin_roots_object = |
+ |
+void FUNCTION_NAME(SecurityContext_SetAlpnProtocols)( |
+ Dart_NativeArguments args) { |
+ SSL_CTX* context = GetSecurityContext(args); |
+ Dart_Handle protocols_handle = |
+ ThrowIfError(Dart_GetNativeArgument(args, 1)); |
+ Dart_Handle is_server_handle = |
ThrowIfError(Dart_GetNativeArgument(args, 2)); |
- // Check that the type is boolean, and get the boolean value from it. |
- bool builtin_roots = true; |
- if (Dart_IsBoolean(builtin_roots_object)) { |
- ThrowIfError(Dart_BooleanValue(builtin_roots_object, &builtin_roots)); |
+ 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( |
- "UseBuiltinRoots argument to SetCertificateDatabase is not a bool")); |
+ "Non-boolean is_server argument passed to SetAlpnProtocols")); |
} |
+} |
+ |
- SSLFilter::InitializeLibrary(certificate_database, password, builtin_roots); |
+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(SecureSocket_PeerCertificate) |
- (Dart_NativeArguments args) { |
- Dart_SetReturnValue(args, GetFilter(args)->PeerCertificate()); |
+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(SecureSocket_FilterPointer)(Dart_NativeArguments args) { |
- intptr_t filter_pointer = reinterpret_cast<intptr_t>(GetFilter(args)); |
- Dart_SetReturnValue(args, Dart_NewInteger(filter_pointer)); |
+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)); |
} |
@@ -314,8 +571,9 @@ CObject* SSLFilter::ProcessFilterRequest(const CObjectArray& request) { |
} |
return result; |
} else { |
- PRErrorCode error_code = PR_GetError(); |
- const char* error_message = PR_ErrorToString(error_code, PR_LANGUAGE_EN); |
+ // TODO(24185): Extract the BoringSSL OS error here and return it. |
+ int error_code = 1; |
+ const char* error_message = "Obsolete PR Error message"; |
CObjectArray* result = new CObjectArray(CObject::NewArray(2)); |
result->SetAt(0, new CObjectInt32(CObject::NewInt32(error_code))); |
result->SetAt(1, new CObjectString(CObject::NewString(error_message))); |
@@ -365,33 +623,24 @@ bool SSLFilter::ProcessAllBuffers(int starts[kNumBuffers], |
ends[i] = end; |
break; |
case kReadEncrypted: |
- // Read data from circular buffer. |
+ 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 = ProcessReadEncryptedBuffer(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 = ProcessReadEncryptedBuffer(start, end); |
- if (bytes < 0) return false; |
- start += bytes; |
- ASSERT(start <= end); |
- } |
- starts[i] = start; |
- break; |
- case kWritePlaintext: |
- if (end < start) { |
- // Data is split into two segments, [start, size) and [0, end). |
- int bytes = ProcessWritePlaintextBuffer(start, size, 0, end); |
- if (bytes < 0) return false; |
- start += bytes; |
- if (start >= size) start -= size; |
- } else { |
- int bytes = ProcessWritePlaintextBuffer(start, end, 0, 0); |
+ int bytes = (i == kReadEncrypted) ? |
+ ProcessReadEncryptedBuffer(start, end) : |
+ ProcessWritePlaintextBuffer(start, end); |
if (bytes < 0) return false; |
start += bytes; |
ASSERT(start <= end); |
@@ -406,47 +655,9 @@ bool SSLFilter::ProcessAllBuffers(int starts[kNumBuffers], |
} |
-static Dart_Handle X509FromCertificate(CERTCertificate* certificate) { |
- PRTime start_validity; |
- PRTime end_validity; |
- SECStatus status = |
- CERT_GetCertTimes(certificate, &start_validity, &end_validity); |
- if (status != SECSuccess) { |
- ThrowPRException("CertificateException", |
- "Cannot get validity times from certificate"); |
- } |
- int64_t start_epoch_ms = start_validity / PR_USEC_PER_MSEC; |
- int64_t end_epoch_ms = end_validity / PR_USEC_PER_MSEC; |
- Dart_Handle subject_name_object = |
- DartUtils::NewString(certificate->subjectName); |
- Dart_Handle issuer_name_object = |
- DartUtils::NewString(certificate->issuerName); |
- Dart_Handle start_epoch_ms_int = Dart_NewInteger(start_epoch_ms); |
- Dart_Handle end_epoch_ms_int = Dart_NewInteger(end_epoch_ms); |
- |
- Dart_Handle date_type = |
- DartUtils::GetDartType(DartUtils::kCoreLibURL, "DateTime"); |
- Dart_Handle from_milliseconds = |
- DartUtils::NewString("fromMillisecondsSinceEpoch"); |
- |
- Dart_Handle start_validity_date = |
- Dart_New(date_type, from_milliseconds, 1, &start_epoch_ms_int); |
- Dart_Handle end_validity_date = |
- Dart_New(date_type, from_milliseconds, 1, &end_epoch_ms_int); |
- |
- Dart_Handle x509_type = |
- DartUtils::GetDartType(DartUtils::kIOLibURL, "X509Certificate"); |
- Dart_Handle arguments[] = { subject_name_object, |
- issuer_name_object, |
- start_validity_date, |
- end_validity_date }; |
- return Dart_New(x509_type, Dart_Null(), 4, arguments); |
-} |
- |
- |
void SSLFilter::Init(Dart_Handle dart_this) { |
if (!library_initialized_) { |
- InitializeLibrary(NULL, "", true, false); |
+ InitializeLibrary(); |
} |
ASSERT(string_start_ == NULL); |
string_start_ = Dart_NewPersistentHandle(DartUtils::NewString("start")); |
@@ -459,7 +670,6 @@ void SSLFilter::Init(Dart_Handle dart_this) { |
ASSERT(bad_certificate_callback_ != NULL); |
InitializeBuffers(dart_this); |
- filter_ = memio_CreateIOLayer(kMemioBufferSize, kMemioBufferSize); |
} |
@@ -507,6 +717,7 @@ void SSLFilter::InitializeBuffers(Dart_Handle dart_this) { |
void SSLFilter::RegisterHandshakeCompleteCallback(Dart_Handle complete) { |
ASSERT(NULL == handshake_complete_); |
handshake_complete_ = Dart_NewPersistentHandle(complete); |
+ |
ASSERT(handshake_complete_ != NULL); |
} |
@@ -519,157 +730,123 @@ void SSLFilter::RegisterBadCertificateCallback(Dart_Handle callback) { |
} |
-char* PasswordCallback(PK11SlotInfo* slot, PRBool retry, void* arg) { |
- if (!retry) { |
- return PL_strdup(static_cast<char*>(arg)); // Freed by NSS internals. |
- } |
- return NULL; |
-} |
- |
- |
-static const char* builtin_roots_module = |
-#if defined(TARGET_OS_LINUX) || defined(TARGET_OS_ANDROID) |
- "name=\"Root Certs\" library=\"libnssckbi.so\""; |
-#elif defined(TARGET_OS_MACOS) |
- "name=\"Root Certs\" library=\"libnssckbi.dylib\""; |
-#elif defined(TARGET_OS_WINDOWS) |
- "name=\"Root Certs\" library=\"nssckbi.dll\""; |
-#else |
-#error Automatic target os detection failed. |
-#endif |
- |
- |
- |
-void SSLFilter::InitializeLibrary(const char* certificate_database, |
- const char* password, |
- bool use_builtin_root_certificates, |
- bool report_duplicate_initialization) { |
+void SSLFilter::InitializeLibrary() { |
MutexLocker locker(mutex_); |
- SECStatus status; |
if (!library_initialized_) { |
- PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); |
- // TODO(whesse): Verify there are no UTF-8 issues here. |
- if (certificate_database == NULL || certificate_database[0] == '\0') { |
- status = NSS_NoDB_Init(NULL); |
- if (status != SECSuccess) { |
- mutex_->Unlock(); // MutexLocker destructor not called when throwing. |
- ThrowPRException("TlsException", |
- "Failed NSS_NoDB_Init call."); |
- } |
- if (use_builtin_root_certificates) { |
- SECMODModule* module = SECMOD_LoadUserModule( |
- const_cast<char*>(builtin_roots_module), NULL, PR_FALSE); |
- if (!module) { |
- mutex_->Unlock(); // MutexLocker destructor not called when throwing. |
- ThrowPRException("TlsException", |
- "Failed to load builtin root certificates."); |
- } |
- } |
- } else { |
- PRUint32 init_flags = NSS_INIT_READONLY; |
- if (!use_builtin_root_certificates) { |
- init_flags |= NSS_INIT_NOMODDB; |
- } |
- status = NSS_Initialize(certificate_database, |
- "", |
- "", |
- SECMOD_DB, |
- init_flags); |
- if (status != SECSuccess) { |
- mutex_->Unlock(); // MutexLocker destructor not called when throwing. |
- ThrowPRException("TlsException", |
- "Failed NSS_Init call."); |
- } |
- password_ = strdup(password); // This one copy persists until Dart exits. |
- PK11_SetPasswordFunc(PasswordCallback); |
- } |
+ SSL_library_init(); |
+ filter_ssl_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL); |
+ ASSERT(filter_ssl_index >= 0); |
library_initialized_ = true; |
+ } |
+} |
- status = NSS_SetDomesticPolicy(); |
- if (status != SECSuccess) { |
- mutex_->Unlock(); // MutexLocker destructor not called when throwing. |
- ThrowPRException("TlsException", |
- "Failed NSS_SetDomesticPolicy call."); |
- } |
- // Enable the same additional ciphers that Chromium does. |
- // See NSSSSLInitSingleton() in Chromium's net/socket/nss_ssl_util.cc. |
- // Explicitly enable exactly those ciphers with keys of at least 80 bits. |
- const PRUint16* const ssl_ciphers = SSL_GetImplementedCiphers(); |
- const PRUint16 num_ciphers = SSL_GetNumImplementedCiphers(); |
- for (int i = 0; i < num_ciphers; i++) { |
- SSLCipherSuiteInfo info; |
- if (SSL_GetCipherSuiteInfo(ssl_ciphers[i], &info, sizeof(info)) == |
- SECSuccess) { |
- bool enabled = (info.effectiveKeyBits >= 80); |
- // Trim the list of cipher suites in order to keep the size of the |
- // ClientHello down. DSS, ECDH, CAMELLIA, SEED, ECC+3DES, and |
- // HMAC-SHA256 cipher suites are disabled. |
- if (info.symCipher == ssl_calg_camellia || |
- info.symCipher == ssl_calg_seed || |
- (info.symCipher == ssl_calg_3des && info.keaType != ssl_kea_rsa) || |
- info.authAlgorithm == ssl_auth_dsa || |
- info.macAlgorithm == ssl_hmac_sha256 || |
- info.nonStandard || |
- strcmp(info.keaTypeName, "ECDH") == 0) { |
- enabled = false; |
- } |
+Dart_Handle SSLFilter::PeerCertificate() { |
+ X509* certificate = SSL_get_peer_certificate(ssl_); |
+ Dart_Handle x509_object = WrappedX509Certificate(certificate); |
+ if (Dart_IsError(x509_object)) { |
+ Dart_PropagateError(x509_object); |
+ } |
+ return x509_object; |
+} |
+ |
- if (ssl_ciphers[i] == TLS_DHE_DSS_WITH_AES_128_CBC_SHA) { |
- // Enabled to allow servers with only a DSA certificate to function. |
- enabled = true; |
+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 |
} |
- SSL_CipherPrefSetDefault(ssl_ciphers[i], enabled); |
} |
+ client_list += client_protocol_length; |
} |
- |
- status = SSL_ConfigServerSessionIDCache(0, 0, 0, NULL); |
- if (status != SECSuccess) { |
- mutex_->Unlock(); // MutexLocker destructor not called when throwing. |
- ThrowPRException("TlsException", |
- "Failed SSL_ConfigServerSessionIDCache call."); |
- } |
- |
- } else if (report_duplicate_initialization) { |
- mutex_->Unlock(); // MutexLocker destructor not called when throwing. |
- // Like ThrowPRException, without adding an OSError. |
- Dart_ThrowException(DartUtils::NewDartIOException("TlsException", |
- "Called SecureSocket.initialize more than once", |
- Dart_Null())); |
+ server_list += protocol_length; |
} |
+ // TODO(23580): Make failure send a fatal alert instead of ignoring ALPN. |
+ return SSL_TLSEXT_ERR_NOACK; |
} |
-SECStatus BadCertificateCallback(void* filter, PRFileDesc* fd) { |
- SSLFilter* ssl_filter = static_cast<SSLFilter*>(filter); |
- Dart_Handle callback = ssl_filter->bad_certificate_callback(); |
- if (Dart_IsNull(callback)) return SECFailure; |
- Dart_Handle x509_object = ssl_filter->PeerCertificate(); |
- Dart_Handle result = Dart_InvokeClosure(callback, 1, &x509_object); |
+// Sets the protocol list for ALPN on a SSL object or a context. |
+static void SetAlpnProtocolList(Dart_Handle protocols_handle, |
+ SSL* ssl, |
+ SSL_CTX* 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)) { |
- ssl_filter->callback_error = result; |
- return SECFailure; |
+ Dart_PropagateError(result); |
} |
- // Our wrapper is guaranteed to return a boolean. |
- bool c_result = DartUtils::GetBooleanValue(result); |
- return c_result ? SECSuccess : SECFailure; |
-} |
+ if (protocols_type != Dart_TypedData_kUint8) { |
+ Dart_TypedDataReleaseData(protocols_handle); |
+ Dart_PropagateError(Dart_NewApiError( |
+ "Unexpected type for protocols (expected valid Uint8List).")); |
+ } |
-Dart_Handle SSLFilter::PeerCertificate() { |
- CERTCertificate* certificate = SSL_PeerCertificate(filter_); |
- if (certificate == NULL) return Dart_Null(); |
- Dart_Handle x509_object = X509FromCertificate(certificate); |
- CERT_DestroyCertificate(certificate); |
- return x509_object; |
+ 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, AlpnCallback, protocol_string_copy); |
+ // TODO(whesse): If this function is called again, free the previous |
+ // protocol_string_copy. It may be better to keep this as a native |
+ // field on the Dart object, since fetching it from the structure is |
+ // not in the public api. Also free this when the context is destroyed. |
+ } 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, protocol_string, protocol_string_len); |
+ } |
+ ASSERT(status == 0); // The function returns a non-standard status. |
+ } |
+ } |
+ Dart_TypedDataReleaseData(protocols_handle); |
} |
-void SSLFilter::Connect(const char* host_name, |
- const RawAddr& raw_addr, |
- int port, |
+void SSLFilter::Connect(const char* hostname, |
+ SSL_CTX* context, |
bool is_server, |
- const char* certificate_name, |
bool request_client_certificate, |
bool require_client_certificate, |
bool send_client_certificate, |
@@ -679,197 +856,131 @@ void SSLFilter::Connect(const char* host_name, |
FATAL("Connect called twice on the same _SecureFilter."); |
} |
- if (!is_server && certificate_name != NULL) { |
- client_certificate_name_ = strdup(certificate_name); |
- } |
+ int status; |
+ int error; |
+ BIO* ssl_side; |
+ status = BIO_new_bio_pair(&ssl_side, 10000, &socket_side_, 10000); |
+ CheckStatus(status, "BIO_new_bio_pair", __LINE__); |
- filter_ = SSL_ImportFD(NULL, filter_); |
- if (filter_ == NULL) { |
- ThrowPRException("TlsException", "Failed SSL_ImportFD call"); |
+ if (context == NULL) { |
+ DART_CHECK_VALID(Dart_ThrowException(DartUtils::NewDartArgumentError( |
+ "Default SecurityContext not implemented, context cannot be null."))); |
} |
- |
- SECStatus status; |
- |
- // Enable ALPN (application layer protocol negogiation) if the caller provides |
- // a valid list of supported protocols. |
- { |
- Dart_TypedData_Type protocols_type; |
- uint8_t* protocol_string = NULL; |
- intptr_t protocol_string_len = 0; |
- |
- 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) { |
- status = SSL_OptionSet(filter_, SSL_ENABLE_ALPN, PR_TRUE); |
- ASSERT(status == SECSuccess); |
- |
- status = SSL_SetNextProtoNego(filter_, |
- protocol_string, |
- protocol_string_len); |
- ASSERT(status == SECSuccess); |
- } |
- |
- Dart_TypedDataReleaseData(protocols_handle); |
+ 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_) { |
+ SetAlpnProtocolList(protocols_handle, ssl_, NULL, false); |
+ // 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_hostflags(certificate_checking_parameters_, 0); |
+ X509_VERIFY_PARAM_set1_host(certificate_checking_parameters_, |
+ hostname_, 0); |
+ // TODO(24186) free hostname_ if it is not freed when SSL is destroyed. |
+ // otherwise, make it a local variable, not a instance field. |
} |
- |
- SSLVersionRange vrange; |
- vrange.min = SSL_LIBRARY_VERSION_3_0; |
- vrange.max = SSL_LIBRARY_VERSION_TLS_1_2; |
- SSL_VersionRangeSet(filter_, &vrange); |
- |
- if (is_server) { |
- CERTCertificate* certificate = NULL; |
- if (strstr(certificate_name, "CN=") != NULL) { |
- // Look up certificate using the distinguished name (DN) certificate_name. |
- CERTCertDBHandle* certificate_database = CERT_GetDefaultCertDB(); |
- if (certificate_database == NULL) { |
- ThrowPRException("CertificateException", |
- "Certificate database cannot be loaded"); |
- } |
- certificate = CERT_FindCertByNameString(certificate_database, |
- const_cast<char*>(certificate_name)); |
- if (certificate == NULL) { |
- ThrowCertificateException( |
- "Cannot find server certificate by distinguished name: %s", |
- certificate_name); |
- } |
- } else { |
- // Look up certificate using the nickname certificate_name. |
- certificate = PK11_FindCertFromNickname( |
- const_cast<char*>(certificate_name), |
- static_cast<void*>(const_cast<char*>(password_))); |
- if (certificate == NULL) { |
- ThrowCertificateException( |
- "Cannot find server certificate by nickname: %s", |
- certificate_name); |
- } |
- } |
- SECKEYPrivateKey* key = PK11_FindKeyByAnyCert( |
- certificate, |
- static_cast<void*>(const_cast<char*>(password_))); |
- if (key == NULL) { |
- CERT_DestroyCertificate(certificate); |
- if (PR_GetError() == -8177) { |
- ThrowPRException("CertificateException", |
- "Certificate database password incorrect"); |
- } else { |
- ThrowCertificateException( |
- "Cannot find private key for certificate %s", |
- certificate_name); |
- } |
+ 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); |
} |
- |
- // kt_rsa (key type RSA) is an enum constant from the NSS libraries. |
- // TODO(whesse): Allow different key types. |
- status = SSL_ConfigSecureServer(filter_, certificate, key, kt_rsa); |
- CERT_DestroyCertificate(certificate); |
- SECKEY_DestroyPrivateKey(key); |
- if (status != SECSuccess) { |
- ThrowCertificateException( |
- "Failed SSL_ConfigSecureServer call with certificate %s", |
- certificate_name); |
+ } 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); |
} |
- |
+ } |
+ if (is_server_) { |
if (request_client_certificate) { |
- status = SSL_OptionSet(filter_, SSL_REQUEST_CERTIFICATE, PR_TRUE); |
- if (status != SECSuccess) { |
- ThrowPRException("TlsException", |
- "Failed SSL_OptionSet(REQUEST_CERTIFICATE) call"); |
- } |
- status = SSL_OptionSet(filter_, |
- SSL_REQUIRE_CERTIFICATE, |
- require_client_certificate); |
- if (status != SECSuccess) { |
- ThrowPRException("TlsException", |
- "Failed SSL_OptionSet(REQUIRE_CERTIFICATE) call"); |
- } |
+ // TODO(24069): Handle client certificates on server side. |
+ Dart_ThrowException(DartUtils::NewDartArgumentError( |
+ "requestClientCertificate not implemented.")); |
} |
} else { // Client. |
- if (SSL_SetURL(filter_, host_name) == -1) { |
- ThrowPRException("TlsException", "Failed SetURL call"); |
- } |
if (send_client_certificate) { |
- SSL_SetPKCS11PinArg(filter_, const_cast<char*>(password_)); |
- status = SSL_GetClientAuthDataHook( |
- filter_, |
- NSS_GetClientAuthData, |
- static_cast<void*>(client_certificate_name_)); |
- if (status != SECSuccess) { |
- ThrowPRException("TlsException", |
- "Failed SSL_GetClientAuthDataHook call"); |
- } |
+ // TODO(24070): Handle client certificates on client side. |
+ Dart_ThrowException(DartUtils::NewDartArgumentError( |
+ "sendClientCertificate not implemented.")); |
} |
} |
+ Handshake(); |
+} |
- // Install bad certificate callback, and pass 'this' to it if it is called. |
- status = SSL_BadCertHook(filter_, |
- BadCertificateCallback, |
- static_cast<void*>(this)); |
- |
- status = SSL_ResetHandshake(filter_, is_server); |
- if (status != SECSuccess) { |
- ThrowPRException("TlsException", |
- "Failed SSL_ResetHandshake call"); |
- } |
- // Set the peer address from the address passed. The DNS has already |
- // been done in Dart code, so just use that address. This relies on |
- // following about PRNetAddr: "The raw member of the union is |
- // equivalent to struct sockaddr", which is stated in the NSS |
- // documentation. |
- PRNetAddr peername; |
- memset(&peername, 0, sizeof(peername)); |
- intptr_t len = SocketAddress::GetAddrLength(raw_addr); |
- ASSERT(static_cast<size_t>(len) <= sizeof(peername)); |
- memmove(&peername, &raw_addr.addr, len); |
- |
- // Adjust the address family field for BSD, whose sockaddr |
- // structure has a one-byte length and one-byte address family |
- // field at the beginning. PRNetAddr has a two-byte address |
- // family field at the beginning. |
- peername.raw.family = raw_addr.addr.sa_family; |
- |
- memio_SetPeerName(filter_, &peername); |
+int printErrorCallback(const char *str, size_t len, void *ctx) { |
+ Log::PrintErr("%.*s\n", static_cast<int>(len), str); |
+ return 1; |
} |
- |
void SSLFilter::Handshake() { |
- SECStatus status = SSL_ForceHandshake(filter_); |
- if (status == SECSuccess) { |
+ // Try and push handshake along. |
+ int status; |
+ int error; |
+ 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_LOG_STATUS) Log::Print("SSL_handshake status: %d\n", status); |
+ if (status != 1) { |
+ error = SSL_get_error(ssl_, status); |
+ if (SSL_LOG_STATUS) Log::Print("ERROR: %d\n", error); |
+ ERR_print_errors_cb(printErrorCallback, NULL); |
+ } |
+ if (status == 1) { |
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; |
} |
- } else { |
- if (callback_error != NULL) { |
- Dart_PropagateError(callback_error); |
+ } else if (status == 0) { |
+ if (is_server_) { |
+ ThrowIOException("HandshakeException", |
+ "Handshake error in server"); |
+ } else { |
+ ThrowIOException("HandshakeException", |
+ "Handshake error in client"); |
} |
- PRErrorCode error = PR_GetError(); |
- if (error == PR_WOULD_BLOCK_ERROR) { |
+ } else if (status < 0) { |
+ if (SSL_want_write(ssl_) || SSL_want_read(ssl_)) { |
if (!in_handshake_) { |
in_handshake_ = true; |
} |
} else { |
if (is_server_) { |
- ThrowPRException("HandshakeException", |
+ ThrowIOException("HandshakeException", |
"Handshake error in server"); |
} else { |
- ThrowPRException("HandshakeException", |
+ ThrowIOException("HandshakeException", |
"Handshake error in client"); |
} |
} |
@@ -877,42 +988,13 @@ void SSLFilter::Handshake() { |
} |
void SSLFilter::GetSelectedProtocol(Dart_NativeArguments args) { |
- // Space for the selected protocol. |
- const unsigned int kBufferSize = 256; |
- unsigned char buffer[kBufferSize + 1]; |
- |
- unsigned int outLength = 0; |
- SSLNextProtoState outState; |
- |
- SECStatus status = SSL_GetNextProto( |
- filter_, &outState, buffer, &outLength, kBufferSize); |
- if (status == SECSuccess) { |
- if (outState == SSL_NEXT_PROTO_SELECTED || |
- outState == SSL_NEXT_PROTO_NEGOTIATED) { |
- ASSERT(outLength <= kBufferSize); |
- buffer[outLength] = '\0'; |
- Dart_Handle protocol_string = DartUtils::NewString( |
- reinterpret_cast<const char *>(&buffer[0])); |
- if (Dart_IsError(protocol_string)) { |
- ThrowPRException("HandshakeException", |
- "Protocol selected via ALPN, unable to get protocol " |
- "string."); |
- } else { |
- Dart_SetReturnValue(args, protocol_string); |
- } |
- } else if (outState == SSL_NEXT_PROTO_NO_OVERLAP) { |
- ThrowPRException("HandshakeException", |
- "Client and Server could not agree upon a protocol"); |
- } else if (outState == SSL_NEXT_PROTO_NO_SUPPORT) { |
- // A value of `null` denotes that the client did not support protocol |
- // negogiation. |
- Dart_SetReturnValue(args, Dart_Null()); |
- } else { |
- UNREACHABLE(); |
- } |
+ const uint8_t* protocol; |
+ unsigned length; |
+ SSL_get0_alpn_selected(ssl_, &protocol, &length); |
+ if (length == 0) { |
+ Dart_SetReturnValue(args, Dart_Null()); |
} else { |
- ThrowPRException("HandshakeException", |
- "Could not retrieve selected protocol via ALPN"); |
+ Dart_SetReturnValue(args, Dart_NewStringFromUTF8(protocol, length)); |
} |
} |
@@ -920,41 +1002,20 @@ void SSLFilter::GetSelectedProtocol(Dart_NativeArguments args) { |
void SSLFilter::Renegotiate(bool use_session_cache, |
bool request_client_certificate, |
bool require_client_certificate) { |
- SECStatus status; |
// 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; |
- |
- status = SSL_OptionSet(filter_, |
- SSL_REQUEST_CERTIFICATE, |
- request_client_certificate); |
- if (status != SECSuccess) { |
- ThrowPRException("TlsException", |
- "Failure in (Raw)SecureSocket.renegotiate request_client_certificate"); |
- } |
- status = SSL_OptionSet(filter_, |
- SSL_REQUIRE_CERTIFICATE, |
- require_client_certificate); |
- if (status != SECSuccess) { |
- ThrowPRException("TlsException", |
- "Failure in (Raw)SecureSocket.renegotiate require_client_certificate"); |
- } |
- bool flush_cache = !use_session_cache; |
- status = SSL_ReHandshake(filter_, flush_cache); |
- if (status != SECSuccess) { |
- if (is_server_) { |
- ThrowPRException("HandshakeException", |
- "Failure in (Raw)SecureSocket.renegotiate in server"); |
- } else { |
- ThrowPRException("HandshakeException", |
- "Failure in (Raw)SecureSocket.renegotiate in client"); |
- } |
- } |
+ // TODO(24070, 24069): Implement setting the client certificate parameters, |
+ // and triggering rehandshake. |
} |
void SSLFilter::Destroy() { |
+ if (ssl_ != NULL) { |
+ SSL_free(ssl_); |
+ ssl_ = NULL; |
+ } |
for (int i = 0; i < kNumBuffers; ++i) { |
Dart_DeletePersistentHandle(dart_buffer_objects_[i]); |
delete[] buffers_[i]; |
@@ -963,25 +1024,21 @@ void SSLFilter::Destroy() { |
Dart_DeletePersistentHandle(string_length_); |
Dart_DeletePersistentHandle(handshake_complete_); |
Dart_DeletePersistentHandle(bad_certificate_callback_); |
- free(client_certificate_name_); |
- |
- PR_Close(filter_); |
} |
-intptr_t SSLFilter::ProcessReadPlaintextBuffer(int start, int end) { |
+/* 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 = PR_Read(filter_, |
- buffers_[kReadPlaintext] + start, |
- length); |
+ bytes_processed = SSL_read( |
+ ssl_, |
+ reinterpret_cast<char*>((buffers_[kReadPlaintext] + start)), |
+ length); |
if (bytes_processed < 0) { |
- ASSERT(bytes_processed == -1); |
- PRErrorCode pr_error = PR_GetError(); |
- if (PR_WOULD_BLOCK_ERROR != pr_error) { |
- return -1; |
- } |
+ int error = SSL_get_error(ssl_, bytes_processed); |
+ USE(error); |
bytes_processed = 0; |
} |
} |
@@ -989,70 +1046,58 @@ intptr_t SSLFilter::ProcessReadPlaintextBuffer(int start, int end) { |
} |
-intptr_t SSLFilter::ProcessWritePlaintextBuffer(int start1, int end1, |
- int start2, int end2) { |
- PRIOVec ranges[2]; |
- uint8_t* buffer = buffers_[kWritePlaintext]; |
- ranges[0].iov_base = reinterpret_cast<char*>(buffer + start1); |
- ranges[0].iov_len = end1 - start1; |
- ranges[1].iov_base = reinterpret_cast<char*>(buffer + start2); |
- ranges[1].iov_len = end2 - start2; |
- int bytes_processed = PR_Writev(filter_, ranges, 2, PR_INTERVAL_NO_TIMEOUT); |
+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) { |
- ASSERT(bytes_processed == -1); |
- PRErrorCode pr_error = PR_GetError(); |
- if (PR_WOULD_BLOCK_ERROR != pr_error) { |
- return -1; |
+ if (SSL_LOG_DATA) { |
+ Log::Print("SSL_write returned error %d\n", bytes_processed); |
} |
- bytes_processed = 0; |
+ return 0; |
} |
return bytes_processed; |
} |
-intptr_t SSLFilter::ProcessReadEncryptedBuffer(int start, int end) { |
+/* 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) { |
- memio_Private* secret = memio_GetSecret(filter_); |
- uint8_t* filter_buf; |
- int free_bytes = memio_GetReadParams(secret, &filter_buf); |
- bytes_processed = dart::Utils::Minimum(length, free_bytes); |
- memmove(filter_buf, buffers_[kReadEncrypted] + start, bytes_processed); |
- memio_PutReadResult(secret, bytes_processed); |
+ 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; |
} |
-intptr_t SSLFilter::ProcessWriteEncryptedBuffer(int start, int end) { |
+int SSLFilter::ProcessWriteEncryptedBuffer(int start, int end) { |
int length = end - start; |
int bytes_processed = 0; |
if (length > 0) { |
- uint8_t* buffer = buffers_[kWriteEncrypted]; |
- const uint8_t* buf1; |
- const uint8_t* buf2; |
- unsigned int len1; |
- unsigned int len2; |
- memio_Private* secret = memio_GetSecret(filter_); |
- int status = memio_GetWriteParams(secret, &buf1, &len1, &buf2, &len2); |
- if (status != 0) { |
- return -1; |
- } |
- int bytes_to_send = |
- dart::Utils::Minimum(len1, static_cast<unsigned>(length)); |
- if (bytes_to_send > 0) { |
- memmove(buffer + start, buf1, bytes_to_send); |
- bytes_processed = bytes_to_send; |
- } |
- bytes_to_send = dart::Utils::Minimum(len2, |
- static_cast<unsigned>(length - bytes_processed)); |
- if (bytes_to_send > 0) { |
- memmove(buffer + start + bytes_processed, buf2, bytes_to_send); |
- bytes_processed += bytes_to_send; |
- } |
- if (bytes_processed > 0) { |
- memio_PutWriteResult(secret, bytes_processed); |
+ 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; |