// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. #if !defined(DART_IO_SECURE_SOCKET_DISABLED) #include "bin/security_context.h" #include #include #include #include #include #include "platform/globals.h" #include "bin/directory.h" #include "bin/file.h" #include "bin/secure_socket_filter.h" #include "bin/secure_socket_utils.h" #include "platform/syslog.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 { const char* SSLCertContext::root_certs_file_ = nullptr; const char* SSLCertContext::root_certs_cache_ = nullptr; bool SSLCertContext::long_ssl_cert_evaluation_ = false; bool SSLCertContext::bypass_trusting_system_roots_ = false; int SSLCertContext::CertificateCallback(int preverify_ok, X509_STORE_CTX* store_ctx) { if (preverify_ok == 1) { return 1; } Dart_Isolate isolate = Dart_CurrentIsolate(); if (isolate == nullptr) { 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(X509_STORE_CTX_get_ex_data(store_ctx, ssl_index)); SSLFilter* filter = static_cast( 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 != nullptr) { X509_up_ref(certificate); } Dart_Handle args[1]; args[0] = X509Helper::WrappedX509Certificate(certificate); if (Dart_IsError(args[0])) { filter->callback_error = args[0]; return 0; } Dart_Handle result = Dart_InvokeClosure(callback, 1, args); if (!Dart_IsError(result) && !Dart_IsBoolean(result)) { result = Dart_NewUnhandledExceptionError(DartUtils::NewDartIOException( "HandshakeException", "BadCertificateCallback returned a value that was not a boolean", Dart_Null())); } // See SSLFilter::Handshake for the semantics of filter->callback_error. if (Dart_IsError(result) && filter->callback_error == nullptr) { filter->callback_error = result; return 0; } return static_cast(DartUtils::GetBooleanValue(result)); } void SSLCertContext::KeyLogCallback(const SSL* ssl, const char* line) { SSLFilter* filter = static_cast( SSL_get_ex_data(ssl, SSLFilter::filter_ssl_index)); Dart_Port port = filter->key_log_port(); if (port != ILLEGAL_PORT) { DartUtils::PostString(port, line); } } SSLCertContext* SSLCertContext::GetSecurityContext(Dart_NativeArguments args) { SSLCertContext* context; Dart_Handle dart_this = ThrowIfError(Dart_GetNativeArgument(args, 0)); ASSERT(Dart_IsInstance(dart_this)); ThrowIfError(Dart_GetNativeInstanceField( dart_this, SSLCertContext::kSecurityContextNativeFieldIndex, reinterpret_cast(&context))); if (context == nullptr) { Dart_PropagateError(Dart_NewUnhandledExceptionError( DartUtils::NewInternalError("No native peer"))); } return context; } static void DeleteSecurityContext(void* isolate_data, void* context_pointer) { SSLCertContext* context = static_cast(context_pointer); context->Release(); } static Dart_Handle SetSecurityContext(Dart_NativeArguments args, SSLCertContext* context) { Dart_Handle dart_this = Dart_GetNativeArgument(args, 0); RETURN_IF_ERROR(dart_this); ASSERT(Dart_IsInstance(dart_this)); Dart_Handle err = Dart_SetNativeInstanceField( dart_this, SSLCertContext::kSecurityContextNativeFieldIndex, reinterpret_cast(context)); RETURN_IF_ERROR(err); Dart_NewFinalizableHandle(dart_this, context, SSLCertContext::kApproximateSize, DeleteSecurityContext); return Dart_Null(); } static void ReleaseCertificate(void* isolate_data, void* context_pointer) { X509* cert = reinterpret_cast(context_pointer); X509_free(cert); } static intptr_t EstimateX509Size(X509* certificate) { intptr_t length = i2d_X509(certificate, nullptr); length = length > 0 ? length : 0; // An X509 is a tree of structures, which are either opaque or will be opaque // in the future. Estimate the overhead to 512 bytes by rounding up // sizeof(X509) + sizeof(X509_CINF). return length + 512; } // Returns the handle for a Dart object wrapping the X509 certificate object. // The caller should own a reference to the X509 object whose reference count // won't drop to zero before the ReleaseCertificate finalizer runs. Dart_Handle X509Helper::WrappedX509Certificate(X509* certificate) { if (certificate == nullptr) { 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[] = {nullptr}; Dart_Handle result = Dart_New(x509_type, DartUtils::NewString("_"), 0, arguments); if (Dart_IsError(result)) { X509_free(certificate); return result; } ASSERT(Dart_IsInstance(result)); Dart_Handle status = Dart_SetNativeInstanceField(result, SSLCertContext::kX509NativeFieldIndex, reinterpret_cast(certificate)); if (Dart_IsError(status)) { X509_free(certificate); return status; } const intptr_t approximate_size_of_certificate = EstimateX509Size(certificate); ASSERT(approximate_size_of_certificate > 0); Dart_NewFinalizableHandle(result, reinterpret_cast(certificate), approximate_size_of_certificate, ReleaseCertificate); return result; } static int SetTrustedCertificatesBytesPKCS12(SSL_CTX* context, ScopedMemBIO* bio, const char* password) { CBS cbs; CBS_init(&cbs, bio->data(), bio->length()); EVP_PKEY* key = nullptr; ScopedX509Stack cert_stack(sk_X509_new_null()); int status = PKCS12_get_key_and_certs(&key, cert_stack.get(), &cbs, password); if (status == 0) { return status; } X509_STORE* store = SSL_CTX_get_cert_store(context); X509* ca; while ((ca = sk_X509_shift(cert_stack.get())) != nullptr) { 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 = nullptr; while ((cert = PEM_read_bio_X509(bio, nullptr, nullptr, nullptr)) != nullptr) { status = X509_STORE_add_cert(store, cert); // X509_STORE_add_cert increments the reference count of cert on success. X509_free(cert); if (status == 0) { return status; } } // If no PEM start line is found, it means that we read to the end of the // file, or that the file isn't PEM. In the first case, status will be // non-zero indicating success. In the second case, status will be 0, // indicating that we should try to read as PKCS12. If there is some other // error, we return it up to the caller. return SecureSocketUtils::NoPEMStartLine() ? status : 0; } void SSLCertContext::SetTrustedCertificatesBytes(Dart_Handle cert_bytes, const char* password) { int status = 0; { ScopedMemBIO bio(cert_bytes); status = SetTrustedCertificatesBytesPEM(context(), bio.bio()); if (status == 0) { if (SecureSocketUtils::NoPEMStartLine()) { ERR_clear_error(); BIO_reset(bio.bio()); status = SetTrustedCertificatesBytesPKCS12(context(), &bio, password); } } else { // The PEM file was successfully parsed. ERR_clear_error(); } } SecureSocketUtils::CheckStatus(status, "TlsException", "Failure trusting builtin roots"); } static int SetClientAuthoritiesPKCS12(SSL_CTX* context, ScopedMemBIO* bio, const char* password) { CBS cbs; CBS_init(&cbs, bio->data(), bio->length()); EVP_PKEY* key = nullptr; ScopedX509Stack cert_stack(sk_X509_new_null()); int status = PKCS12_get_key_and_certs(&key, cert_stack.get(), &cbs, password); if (status == 0) { return status; } X509* ca; while ((ca = sk_X509_shift(cert_stack.get())) != nullptr) { 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 = nullptr; while ((cert = PEM_read_bio_X509(bio, nullptr, nullptr, nullptr)) != nullptr) { status = SSL_CTX_add_client_CA(context, cert); X509_free(cert); // The name has been extracted. if (status == 0) { return status; } } return SecureSocketUtils::NoPEMStartLine() ? status : 0; } static int SetClientAuthorities(SSL_CTX* context, ScopedMemBIO* bio, const char* password) { int status = SetClientAuthoritiesPEM(context, bio->bio()); if (status == 0) { if (SecureSocketUtils::NoPEMStartLine()) { ERR_clear_error(); BIO_reset(bio->bio()); status = SetClientAuthoritiesPKCS12(context, bio, password); } } else { // The PEM file was successfully parsed. ERR_clear_error(); } return status; } void SSLCertContext::SetClientAuthoritiesBytes( Dart_Handle client_authorities_bytes, const char* password) { int status; { ScopedMemBIO bio(client_authorities_bytes); status = SetClientAuthorities(context(), &bio, password); } SecureSocketUtils::CheckStatus(status, "TlsException", "Failure in setClientAuthoritiesBytes"); } void SSLCertContext::LoadRootCertFile(const char* file) { if (SSL_LOG_STATUS) { Syslog::Print("Looking for trusted roots in %s\n", file); } if (!File::Exists(nullptr, file)) { SecureSocketUtils::ThrowIOException( -1, "TlsException", "Failed to find root cert file", nullptr); } int status = SSL_CTX_load_verify_locations(context(), file, nullptr); SecureSocketUtils::CheckStatus(status, "TlsException", "Failure trusting builtin roots"); if (SSL_LOG_STATUS) { Syslog::Print("Trusting roots from: %s\n", file); } } void SSLCertContext::AddCompiledInCerts() { if (root_certificates_pem == nullptr) { if (SSL_LOG_STATUS) { Syslog::Print("Missing compiled-in roots\n"); } return; } X509_STORE* store = SSL_CTX_get_cert_store(context()); BIO* roots_bio = BIO_new_mem_buf(const_cast(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, nullptr, nullptr, nullptr)) != nullptr) { int status = X509_STORE_add_cert(store, root_cert); // X509_STORE_add_cert increments the reference count of cert on success. X509_free(root_cert); if (status == 0) { break; } } BIO_free(roots_bio); // If there is an error here, it must be the error indicating that we are done // reading PEM certificates. ASSERT((ERR_peek_error() == 0) || SecureSocketUtils::NoPEMStartLine()); ERR_clear_error(); } void SSLCertContext::LoadRootCertCache(const char* cache) { if (SSL_LOG_STATUS) { Syslog::Print("Looking for trusted roots in %s\n", cache); } if (Directory::Exists(nullptr, cache) != Directory::EXISTS) { SecureSocketUtils::ThrowIOException( -1, "TlsException", "Failed to find root cert cache", nullptr); } int status = SSL_CTX_load_verify_locations(context(), nullptr, cache); SecureSocketUtils::CheckStatus(status, "TlsException", "Failure trusting builtin roots"); if (SSL_LOG_STATUS) { Syslog::Print("Trusting roots from: %s\n", cache); } } int PasswordCallback(char* buf, int size, int rwflag, void* userdata) { char* password = static_cast(userdata); ASSERT(size == PEM_BUFSIZE); strncpy(buf, password, size); return strlen(password); } static EVP_PKEY* GetPrivateKeyPKCS12(BIO* bio, const char* password) { ScopedPKCS12 p12(d2i_PKCS12_bio(bio, nullptr)); if (p12.get() == nullptr) { return nullptr; } EVP_PKEY* key = nullptr; X509* cert = nullptr; STACK_OF(X509)* ca_certs = nullptr; int status = PKCS12_parse(p12.get(), password, &key, &cert, &ca_certs); if (status == 0) { return nullptr; } // 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, nullptr, PasswordCallback, const_cast(password)); if (key == nullptr) { // We try reading data as PKCS12 only if reading as PEM was unsuccessful and // if there is no indication that the data is malformed PEM. We assume the // data is malformed PEM if it contains the start line, i.e. a line // with ----- BEGIN. if (SecureSocketUtils::NoPEMStartLine()) { // Reset the bio, and clear the error from trying to read as PEM. ERR_clear_error(); BIO_reset(bio); // Try to decode as PKCS12. key = GetPrivateKeyPKCS12(bio, password); } } return key; } const char* SSLCertContext::GetPasswordArgument(Dart_NativeArguments args, intptr_t index) { Dart_Handle password_object = ThrowIfError(Dart_GetNativeArgument(args, index)); const char* password = nullptr; if (Dart_IsString(password_object)) { ThrowIfError(Dart_StringToCString(password_object, &password)); if (strlen(password) > PEM_BUFSIZE - 1) { Dart_ThrowException(DartUtils::NewDartArgumentError( "Password length is greater than 1023 (PEM_BUFSIZE)")); } } else if (Dart_IsNull(password_object)) { password = ""; } else { Dart_ThrowException( DartUtils::NewDartArgumentError("Password is not a String or null")); } return password; } int AlpnCallback(SSL* ssl, const uint8_t** out, uint8_t* outlen, const uint8_t* in, unsigned int inlen, void* arg) { // 'in' and 'arg' are sequences of (length, data) strings with 1-byte lengths. // 'arg' is 0-terminated. Finds the first string in 'arg' that is in 'in'. uint8_t* server_list = static_cast(arg); while (*server_list != 0) { uint8_t protocol_length = *server_list++; const uint8_t* client_list = in; while (client_list < in + inlen) { uint8_t client_protocol_length = *client_list++; if (client_protocol_length == protocol_length) { if (0 == memcmp(server_list, client_list, protocol_length)) { *out = client_list; *outlen = client_protocol_length; return SSL_TLSEXT_ERR_OK; // Success } } client_list += client_protocol_length; } server_list += protocol_length; } // TODO(23580): Make failure send a fatal alert instead of ignoring ALPN. return SSL_TLSEXT_ERR_NOACK; } // Sets the protocol list for ALPN on a SSL object or a context. void SSLCertContext::SetAlpnProtocolList(Dart_Handle protocols_handle, SSL* ssl, SSLCertContext* context, bool is_server) { // Enable ALPN (application layer protocol negotiation) if the caller provides // a valid list of supported protocols. Dart_TypedData_Type protocols_type; uint8_t* protocol_string = nullptr; uint8_t* protocol_string_copy = nullptr; intptr_t protocol_string_len = 0; int status; Dart_Handle result = Dart_TypedDataAcquireData( protocols_handle, &protocols_type, reinterpret_cast(&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 != nullptr); ASSERT(ssl == nullptr); // 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(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 != nullptr) { ASSERT(context == nullptr); status = SSL_set_alpn_protos(ssl, protocol_string, protocol_string_len); } else { ASSERT(context != nullptr); ASSERT(ssl == nullptr); status = SSL_CTX_set_alpn_protos(context->context(), protocol_string, protocol_string_len); } ASSERT(status == 0); // The function returns a non-standard status. } } Dart_TypedDataReleaseData(protocols_handle); } static int UseChainBytesPKCS12(SSL_CTX* context, ScopedMemBIO* bio, const char* password) { CBS cbs; CBS_init(&cbs, bio->data(), bio->length()); EVP_PKEY* key = nullptr; ScopedX509Stack certs(sk_X509_new_null()); int status = PKCS12_get_key_and_certs(&key, certs.get(), &cbs, password); if (status == 0) { return status; } X509* ca = sk_X509_shift(certs.get()); status = SSL_CTX_use_certificate(context, ca); if (ERR_peek_error() != 0) { // Key/certificate mismatch doesn't imply status is 0. status = 0; } X509_free(ca); if (status == 0) { return status; } SSL_CTX_clear_chain_certs(context); while ((ca = sk_X509_shift(certs.get())) != nullptr) { 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, nullptr, nullptr, nullptr)); if (x509.get() == nullptr) { 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, nullptr, nullptr, nullptr)) != nullptr) { status = SSL_CTX_add0_chain_cert(context, ca); // SSL_CTX_add0_chain_cert does not inc ref count, so don't free unless the // call fails. if (status == 0) { X509_free(ca); return status; } // Note that we must not free `ca` if it was successfully added to the // chain. We must free the main certificate x509, though since its reference // count is increased by SSL_CTX_use_certificate. } return SecureSocketUtils::NoPEMStartLine() ? status : 0; } static int UseChainBytes(SSL_CTX* context, ScopedMemBIO* bio, const char* password) { int status = UseChainBytesPEM(context, bio->bio()); if (status == 0) { if (SecureSocketUtils::NoPEMStartLine()) { ERR_clear_error(); BIO_reset(bio->bio()); status = UseChainBytesPKCS12(context, bio, password); } } else { // The PEM file was successfully read. ERR_clear_error(); } return status; } int SSLCertContext::UseCertificateChainBytes(Dart_Handle cert_chain_bytes, const char* password) { ScopedMemBIO bio(cert_chain_bytes); return UseChainBytes(context(), &bio, password); } static X509* GetX509Certificate(Dart_NativeArguments args) { X509* certificate = nullptr; Dart_Handle dart_this = ThrowIfError(Dart_GetNativeArgument(args, 0)); ASSERT(Dart_IsInstance(dart_this)); ThrowIfError(Dart_GetNativeInstanceField( dart_this, SSLCertContext::kX509NativeFieldIndex, reinterpret_cast(&certificate))); if (certificate == nullptr) { Dart_PropagateError(Dart_NewUnhandledExceptionError( DartUtils::NewInternalError("No native peer"))); } return certificate; } Dart_Handle X509Helper::GetDer(Dart_NativeArguments args) { X509* certificate = GetX509Certificate(args); // When the second argument is nullptr, i2d_X509() returns the length of the // DER encoded cert in bytes. intptr_t length = i2d_X509(certificate, nullptr); Dart_Handle cert_handle = Dart_NewTypedData(Dart_TypedData_kUint8, length); if (Dart_IsError(cert_handle)) { Dart_PropagateError(cert_handle); } Dart_TypedData_Type typ; void* dart_cert_bytes = nullptr; Dart_Handle status = Dart_TypedDataAcquireData(cert_handle, &typ, &dart_cert_bytes, &length); if (Dart_IsError(status)) { Dart_PropagateError(status); } // When the second argument points to a non-nullptr buffer address, // i2d_X509 fills that buffer with the DER encoded cert data and increments // the buffer pointer. unsigned char* tmp = static_cast(dart_cert_bytes); const intptr_t written_length = i2d_X509(certificate, &tmp); ASSERT(written_length <= length); if (written_length < 0) { Dart_TypedDataReleaseData(cert_handle); SecureSocketUtils::ThrowIOException( -1, "TlsException", "Failed to get certificate bytes", nullptr); // SecureSocketUtils::ThrowIOException() does not return. } status = Dart_TypedDataReleaseData(cert_handle); if (Dart_IsError(status)) { Dart_PropagateError(status); } return cert_handle; } Dart_Handle X509Helper::GetPem(Dart_NativeArguments args) { X509* certificate = GetX509Certificate(args); BIO* cert_bio = BIO_new(BIO_s_mem()); intptr_t status = PEM_write_bio_X509(cert_bio, certificate); if (status == 0) { BIO_free(cert_bio); SecureSocketUtils::ThrowIOException( -1, "TlsException", "Failed to write certificate to PEM", nullptr); // SecureSocketUtils::ThrowIOException() does not return. } BUF_MEM* mem = nullptr; BIO_get_mem_ptr(cert_bio, &mem); Dart_Handle pem_string = Dart_NewStringFromUTF8( reinterpret_cast(mem->data), mem->length); BIO_free(cert_bio); if (Dart_IsError(pem_string)) { Dart_PropagateError(pem_string); } return pem_string; } Dart_Handle X509Helper::GetSha1(Dart_NativeArguments args) { unsigned char sha1_bytes[EVP_MAX_MD_SIZE]; X509* certificate = GetX509Certificate(args); const EVP_MD* hash_type = EVP_sha1(); unsigned int sha1_size; intptr_t status = X509_digest(certificate, hash_type, sha1_bytes, &sha1_size); if (status == 0) { SecureSocketUtils::ThrowIOException( -1, "TlsException", "Failed to compute certificate's sha1", nullptr); // SecureSocketUtils::ThrowIOException() does not return. } Dart_Handle sha1_handle = Dart_NewTypedData(Dart_TypedData_kUint8, sha1_size); if (Dart_IsError(sha1_handle)) { Dart_PropagateError(sha1_handle); } Dart_TypedData_Type typ; void* dart_sha1_bytes; intptr_t length; Dart_Handle result = Dart_TypedDataAcquireData(sha1_handle, &typ, &dart_sha1_bytes, &length); if (Dart_IsError(result)) { Dart_PropagateError(result); } memmove(dart_sha1_bytes, sha1_bytes, length); result = Dart_TypedDataReleaseData(sha1_handle); if (Dart_IsError(result)) { Dart_PropagateError(result); } return sha1_handle; } Dart_Handle X509Helper::GetSubject(Dart_NativeArguments args) { X509* certificate = GetX509Certificate(args); X509_NAME* subject = X509_get_subject_name(certificate); char* subject_string = X509_NAME_oneline(subject, nullptr, 0); if (subject_string == nullptr) { Dart_ThrowException(DartUtils::NewDartArgumentError( "X509.subject failed to find subject's common name.")); } Dart_Handle subject_handle = Dart_NewStringFromCString(subject_string); OPENSSL_free(subject_string); return subject_handle; } Dart_Handle X509Helper::GetIssuer(Dart_NativeArguments args) { X509* certificate = GetX509Certificate(args); X509_NAME* issuer = X509_get_issuer_name(certificate); char* issuer_string = X509_NAME_oneline(issuer, nullptr, 0); if (issuer_string == nullptr) { Dart_ThrowException(DartUtils::NewDartArgumentError( "X509.issuer failed to find issuer's common name.")); } Dart_Handle issuer_handle = Dart_NewStringFromCString(issuer_string); OPENSSL_free(issuer_string); return issuer_handle; } static Dart_Handle ASN1TimeToMilliseconds(ASN1_TIME* aTime) { ASN1_UTCTIME* epoch_start = ASN1_UTCTIME_new(); ASN1_UTCTIME_set_string(epoch_start, "700101000000Z"); int days; int seconds; int result = ASN1_TIME_diff(&days, &seconds, epoch_start, aTime); ASN1_UTCTIME_free(epoch_start); if (result != 1) { // TODO(whesse): Propagate an error to Dart. Syslog::PrintErr("ASN1Time error %d\n", result); } return Dart_NewInteger((86400LL * days + seconds) * 1000LL); } Dart_Handle X509Helper::GetStartValidity(Dart_NativeArguments args) { X509* certificate = GetX509Certificate(args); ASN1_TIME* not_before = X509_get_notBefore(certificate); return ASN1TimeToMilliseconds(not_before); } Dart_Handle X509Helper::GetEndValidity(Dart_NativeArguments args) { X509* certificate = GetX509Certificate(args); ASN1_TIME* not_after = X509_get_notAfter(certificate); return ASN1TimeToMilliseconds(not_after); } void FUNCTION_NAME(SecurityContext_UsePrivateKeyBytes)( Dart_NativeArguments args) { SSLCertContext* context = SSLCertContext::GetSecurityContext(args); const char* password = SSLCertContext::GetPasswordArgument(args, 2); int status; EVP_PKEY* key; { ScopedMemBIO bio(ThrowIfError(Dart_GetNativeArgument(args, 1))); key = GetPrivateKey(bio.bio(), password); } if (key == nullptr) { Dart_ThrowException(DartUtils::NewDartArgumentError( "Expected private key, but none was found")); } status = SSL_CTX_use_PrivateKey(context->context(), key); // SSL_CTX_use_PrivateKey increments the reference count of key on success, // so we have to call EVP_PKEY_free on both success and failure. EVP_PKEY_free(key); // TODO(24184): Handle different expected errors here - file missing, // incorrect password, file not a PEM, and throw exceptions. // SecureSocketUtils::CheckStatus should also throw an exception in uncaught // cases. SecureSocketUtils::CheckStatus(status, "TlsException", "Failure in usePrivateKeyBytes"); } void FUNCTION_NAME(SecurityContext_Allocate)(Dart_NativeArguments args) { SSLFilter::InitializeLibrary(); SSL_CTX* ctx = SSL_CTX_new(TLS_method()); SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, SSLCertContext::CertificateCallback); SSL_CTX_set_keylog_callback(ctx, SSLCertContext::KeyLogCallback); // If we change the minimum protocol version here, then the documentation // for `SecurityContext.minimumTlsProtocolVersion` must also be changed. SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION); SSL_CTX_set_cipher_list(ctx, "HIGH:MEDIUM"); SSLCertContext* context = new SSLCertContext(ctx); Dart_Handle err = SetSecurityContext(args, context); if (Dart_IsError(err)) { delete context; Dart_PropagateError(err); } } void FUNCTION_NAME(SecurityContext_SetTrustedCertificatesBytes)( Dart_NativeArguments args) { SSLCertContext* context = SSLCertContext::GetSecurityContext(args); Dart_Handle cert_bytes = ThrowIfError(Dart_GetNativeArgument(args, 1)); const char* password = SSLCertContext::GetPasswordArgument(args, 2); ASSERT(context != nullptr); ASSERT(password != nullptr); context->SetTrustedCertificatesBytes(cert_bytes, password); } void FUNCTION_NAME(SecurityContext_SetClientAuthoritiesBytes)( Dart_NativeArguments args) { SSLCertContext* context = SSLCertContext::GetSecurityContext(args); Dart_Handle client_authorities_bytes = ThrowIfError(Dart_GetNativeArgument(args, 1)); const char* password = SSLCertContext::GetPasswordArgument(args, 2); ASSERT(context != nullptr); ASSERT(password != nullptr); context->SetClientAuthoritiesBytes(client_authorities_bytes, password); } void FUNCTION_NAME(SecurityContext_UseCertificateChainBytes)( Dart_NativeArguments args) { SSLCertContext* context = SSLCertContext::GetSecurityContext(args); Dart_Handle cert_chain_bytes = ThrowIfError(Dart_GetNativeArgument(args, 1)); const char* password = SSLCertContext::GetPasswordArgument(args, 2); ASSERT(context != nullptr); ASSERT(password != nullptr); int status = context->UseCertificateChainBytes(cert_chain_bytes, password); SecureSocketUtils::CheckStatus(status, "TlsException", "Failure in useCertificateChainBytes"); } void FUNCTION_NAME(SecurityContext_TrustBuiltinRoots)( Dart_NativeArguments args) { SSLCertContext* context = SSLCertContext::GetSecurityContext(args); ASSERT(context != nullptr); context->TrustBuiltinRoots(); } void FUNCTION_NAME(SecurityContext_SetAllowTlsRenegotiation)( Dart_NativeArguments args) { SSLCertContext* context = SSLCertContext::GetSecurityContext(args); Dart_Handle allow_tls_handle = ThrowIfError(Dart_GetNativeArgument(args, 1)); ASSERT(context != nullptr); ASSERT(allow_tls_handle != nullptr); if (!Dart_IsBoolean(allow_tls_handle)) { Dart_ThrowException(DartUtils::NewDartArgumentError( "Non-boolean argument passed to SetAllowTlsRenegotiation")); } bool allow = DartUtils::GetBooleanValue(allow_tls_handle); context->set_allow_tls_renegotiation(allow); } void FUNCTION_NAME(SecurityContext_SetMinimumProtocolVersion)( Dart_NativeArguments args) { SSLCertContext* context = SSLCertContext::GetSecurityContext(args); Dart_Handle protocol_version_handle = ThrowIfError(Dart_GetNativeArgument(args, 1)); if (!Dart_IsInteger(protocol_version_handle)) { Dart_ThrowException(DartUtils::NewDartArgumentError( "Non-int argument passed to SetMinimumProtocolVersion")); } int protocol_version = DartUtils::GetIntegerValue(protocol_version_handle); if (SSL_CTX_set_min_proto_version(context->context(), protocol_version) == 0) { Dart_ThrowException(DartUtils::NewDartArgumentError( "Invalid protocol version passed to SetMinimumProtocolVersion")); } } void FUNCTION_NAME(SecurityContext_GetMinimumProtocolVersion)( Dart_NativeArguments args) { SSLCertContext* context = SSLCertContext::GetSecurityContext(args); Dart_SetIntegerReturnValue(args, SSL_CTX_get_min_proto_version(context->context())); } void FUNCTION_NAME(X509_Der)(Dart_NativeArguments args) { Dart_SetReturnValue(args, X509Helper::GetDer(args)); } void FUNCTION_NAME(X509_Pem)(Dart_NativeArguments args) { Dart_SetReturnValue(args, X509Helper::GetPem(args)); } void FUNCTION_NAME(X509_Sha1)(Dart_NativeArguments args) { Dart_SetReturnValue(args, X509Helper::GetSha1(args)); } void FUNCTION_NAME(X509_Subject)(Dart_NativeArguments args) { Dart_SetReturnValue(args, X509Helper::GetSubject(args)); } void FUNCTION_NAME(X509_Issuer)(Dart_NativeArguments args) { Dart_SetReturnValue(args, X509Helper::GetIssuer(args)); } void FUNCTION_NAME(X509_StartValidity)(Dart_NativeArguments args) { Dart_SetReturnValue(args, X509Helper::GetStartValidity(args)); } void FUNCTION_NAME(X509_EndValidity)(Dart_NativeArguments args) { Dart_SetReturnValue(args, X509Helper::GetEndValidity(args)); } void FUNCTION_NAME(SecurityContext_SetAlpnProtocols)( Dart_NativeArguments args) { SSLCertContext* context = SSLCertContext::GetSecurityContext(args); Dart_Handle protocols_handle = ThrowIfError(Dart_GetNativeArgument(args, 1)); Dart_Handle is_server_handle = ThrowIfError(Dart_GetNativeArgument(args, 2)); if (Dart_IsBoolean(is_server_handle)) { bool is_server = DartUtils::GetBooleanValue(is_server_handle); SSLCertContext::SetAlpnProtocolList(protocols_handle, nullptr, context, is_server); } else { Dart_ThrowException(DartUtils::NewDartArgumentError( "Non-boolean is_server argument passed to SetAlpnProtocols")); } } } // namespace bin } // namespace dart #endif // !defined(DART_IO_SECURE_SOCKET_DISABLED)