| // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| #ifndef RUNTIME_BIN_SECURE_SOCKET_UTILS_H_ |
| #define RUNTIME_BIN_SECURE_SOCKET_UTILS_H_ |
| |
| #include <openssl/bio.h> |
| #include <openssl/err.h> |
| #include <openssl/pkcs12.h> |
| #include <openssl/ssl.h> |
| #include <openssl/x509.h> |
| |
| #include "platform/globals.h" |
| |
| #include "bin/dartutils.h" |
| #include "platform/text_buffer.h" |
| |
| namespace dart { |
| namespace bin { |
| |
| const bool SSL_LOG_STATUS = false; |
| const bool SSL_LOG_DATA = false; |
| const bool SSL_LOG_CERTS = false; |
| |
| class SecureSocketUtils : public AllStatic { |
| public: |
| static constexpr int SSL_ERROR_MESSAGE_BUFFER_SIZE = 1000; |
| |
| static void ThrowIOException(int status, |
| const char* exception_type, |
| const char* message, |
| const SSL* ssl); |
| |
| static void CheckStatusSSL(int status, |
| const char* type, |
| const char* message, |
| const SSL* ssl); |
| |
| static void CheckStatus(int status, const char* type, const char* message); |
| |
| static bool IsCurrentTimeInsideCertValidDateRange(X509* root_cert); |
| |
| 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 uint32_t FetchErrorString(const SSL* ssl, TextBuffer* text_buffer); |
| }; |
| |
| // Where the argument to the constructor is the handle for an object |
| // implementing List<int>, this class creates a scope in which a memory-backed |
| // BIO is allocated. Leaving the scope cleans up the BIO and the buffer that |
| // was used to create it. |
| // |
| // Do not make Dart_ API calls while in a ScopedMemBIO. |
| // Do not call Dart_PropagateError while in a ScopedMemBIO. |
| class ScopedMemBIO { |
| public: |
| explicit ScopedMemBIO(Dart_Handle object) { |
| if (!Dart_IsTypedData(object) && !Dart_IsList(object)) { |
| Dart_ThrowException( |
| DartUtils::NewDartArgumentError("Argument is not a List<int>")); |
| } |
| |
| uint8_t* bytes = nullptr; |
| 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 != nullptr); |
| 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_ != nullptr); |
| is_typed_data_ = is_typed_data; |
| } |
| |
| ~ScopedMemBIO() { |
| ASSERT(bio_ != nullptr); |
| if (is_typed_data_) { |
| BIO_free(bio_); |
| ThrowIfError(Dart_TypedDataReleaseData(object_)); |
| } else { |
| BIO_free(bio_); |
| } |
| } |
| |
| BIO* bio() { |
| ASSERT(bio_ != nullptr); |
| return bio_; |
| } |
| |
| uint8_t* data() { return bytes_; } |
| intptr_t length() { return bytes_len_; } |
| |
| 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_ != nullptr) { |
| free_func(obj_); |
| } |
| } |
| |
| T* get() { return obj_; } |
| const T* get() const { return obj_; } |
| |
| T* release() { |
| T* result = obj_; |
| obj_ = nullptr; |
| 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_ != nullptr) { |
| OPENSSL_sk_pop_free_ex(reinterpret_cast<OPENSSL_STACK*>(obj_), |
| call_free_func, free_func); |
| } |
| } |
| |
| T* get() { return obj_; } |
| const T* get() const { return obj_; } |
| |
| T* release() { |
| T* result = obj_; |
| obj_ = nullptr; |
| return result; |
| } |
| |
| private: |
| static void free_func(void* element) { func(reinterpret_cast<E*>(element)); } |
| static void call_free_func(void (*free_func)(void*), void* element) { |
| free_func(element); |
| } |
| |
| T* obj_; |
| |
| DISALLOW_ALLOCATION(); |
| DISALLOW_COPY_AND_ASSIGN(ScopedSSLStackType); |
| }; |
| |
| typedef ScopedSSLType<PKCS12, PKCS12_free> ScopedPKCS12; |
| typedef ScopedSSLType<X509, X509_free> ScopedX509; |
| typedef ScopedSSLStackType<STACK_OF(X509), X509, X509_free> ScopedX509Stack; |
| |
| } // namespace bin |
| } // namespace dart |
| |
| #endif // RUNTIME_BIN_SECURE_SOCKET_UTILS_H_ |