// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. #ifndef RUNTIME_BIN_SECURE_SOCKET_MACOS_H_ #define RUNTIME_BIN_SECURE_SOCKET_MACOS_H_ #if !defined(RUNTIME_BIN_SECURE_SOCKET_H_) #error Do not include secure_socket_macos.h directly. Use secure_socket.h. #endif #include #include #include #include #include #include #include #include "bin/builtin.h" #include "bin/dartutils.h" #include "bin/lockers.h" #include "bin/reference_counting.h" #include "bin/socket.h" #include "bin/thread.h" #include "bin/utils.h" namespace dart { namespace bin { // SSLCertContext wraps the certificates needed for a SecureTransport // connection. Fields are protected by the mutex_ field, and may only be set // once. This is to allow access by both the Dart thread and the IOService // thread. Setters return false if the field was already set. class SSLCertContext : public ReferenceCounted { public: SSLCertContext() : ReferenceCounted(), mutex_(new Mutex()), private_key_(NULL), keychain_(NULL), cert_chain_(NULL), trusted_certs_(NULL), cert_authorities_(NULL), trust_builtin_(false) {} ~SSLCertContext() { { MutexLocker m(mutex_); if (private_key_ != NULL) { CFRelease(private_key_); } if (keychain_ != NULL) { SecKeychainDelete(keychain_); CFRelease(keychain_); } if (cert_chain_ != NULL) { CFRelease(cert_chain_); } if (trusted_certs_ != NULL) { CFRelease(trusted_certs_); } if (cert_authorities_ != NULL) { CFRelease(cert_authorities_); } } delete mutex_; } SecKeyRef private_key() { MutexLocker m(mutex_); return private_key_; } bool set_private_key(SecKeyRef private_key) { MutexLocker m(mutex_); if (private_key_ != NULL) { return false; } private_key_ = private_key; return true; } SecKeychainRef keychain() { MutexLocker m(mutex_); return keychain_; } bool set_keychain(SecKeychainRef keychain) { MutexLocker m(mutex_); if (keychain_ != NULL) { return false; } keychain_ = keychain; return true; } CFArrayRef cert_chain() { MutexLocker m(mutex_); return cert_chain_; } bool set_cert_chain(CFArrayRef cert_chain) { MutexLocker m(mutex_); if (cert_chain_ != NULL) { return false; } cert_chain_ = cert_chain; return true; } CFArrayRef trusted_certs() { MutexLocker m(mutex_); return trusted_certs_; } bool set_trusted_certs(CFArrayRef trusted_certs) { MutexLocker m(mutex_); if (trusted_certs_ != NULL) { return false; } trusted_certs_ = trusted_certs; return true; } CFArrayRef cert_authorities() { MutexLocker m(mutex_); return cert_authorities_; } bool set_cert_authorities(CFArrayRef cert_authorities) { MutexLocker m(mutex_); if (cert_authorities_ != NULL) { return false; } cert_authorities_ = cert_authorities; return true; } bool trust_builtin() { MutexLocker m(mutex_); return trust_builtin_; } void set_trust_builtin(bool trust_builtin) { MutexLocker m(mutex_); trust_builtin_ = trust_builtin; } private: // The context is accessed both by Dart code and the IOService. This mutex // protects all fields. Mutex* mutex_; SecKeyRef private_key_; SecKeychainRef keychain_; // CFArrays of SecCertificateRef. CFArrayRef cert_chain_; CFArrayRef trusted_certs_; CFArrayRef cert_authorities_; bool trust_builtin_; DISALLOW_COPY_AND_ASSIGN(SSLCertContext); }; // SSLFilter encapsulates the SecureTransport code in a filter that communicates // with the containing _SecureFilterImpl Dart object through four shared // ExternalByteArray buffers, for reading and writing plaintext, and // reading and writing encrypted text. The filter handles handshaking // and certificate verification. class SSLFilter : public ReferenceCounted { public: // These enums must agree with those in sdk/lib/io/secure_socket.dart. enum BufferIndex { kReadPlaintext, kWritePlaintext, kReadEncrypted, kWriteEncrypted, kNumBuffers, kFirstEncrypted = kReadEncrypted }; SSLFilter() : ReferenceCounted(), cert_context_(NULL), ssl_context_(NULL), peer_certs_(NULL), string_start_(NULL), string_length_(NULL), handshake_complete_(NULL), bad_certificate_callback_(NULL), in_handshake_(false), connected_(false), bad_cert_(false), is_server_(false), hostname_(NULL) {} ~SSLFilter(); // Callback called by the IOService. static CObject* ProcessFilterRequest(const CObjectArray& request); Dart_Handle Init(Dart_Handle dart_this); void Connect(Dart_Handle dart_this, const char* hostname, SSLCertContext* context, bool is_server, bool request_client_certificate, bool require_client_certificate); void Destroy(); OSStatus CheckHandshake(); void Renegotiate(bool use_session_cache, bool request_client_certificate, bool require_client_certificate); void RegisterHandshakeCompleteCallback(Dart_Handle handshake_complete); void RegisterBadCertificateCallback(Dart_Handle callback); Dart_Handle PeerCertificate(); private: static OSStatus SSLReadCallback(SSLConnectionRef connection, void* data, size_t* data_length); static OSStatus SSLWriteCallback(SSLConnectionRef connection, const void* data, size_t* data_length); static bool isBufferEncrypted(intptr_t i) { return static_cast(i) >= kFirstEncrypted; } Dart_Handle InitializeBuffers(Dart_Handle dart_this); intptr_t GetBufferStart(intptr_t idx) const; intptr_t GetBufferEnd(intptr_t idx) const; void SetBufferStart(intptr_t idx, intptr_t value); void SetBufferEnd(intptr_t idx, intptr_t value); OSStatus ProcessAllBuffers(intptr_t starts[kNumBuffers], intptr_t ends[kNumBuffers], bool in_handshake); OSStatus ProcessReadPlaintextBuffer(intptr_t start, intptr_t end, intptr_t* bytes_processed); OSStatus ProcessWritePlaintextBuffer(intptr_t start, intptr_t end, intptr_t* bytes_processed); // These calls can block on IO, and should only be invoked from // from ProcessAllBuffers from ProcessFilterRequest. OSStatus EvaluatePeerTrust(); OSStatus Handshake(); Dart_Handle InvokeBadCertCallback(SecCertificateRef peer_cert); RetainedPointer cert_context_; SSLContextRef ssl_context_; CFArrayRef peer_certs_; // starts and ends filled in at the start of ProcessAllBuffers. // If these are NULL, then try to get the pointers out of // dart_buffer_objects_. uint8_t* buffers_[kNumBuffers]; intptr_t* buffer_starts_[kNumBuffers]; intptr_t* buffer_ends_[kNumBuffers]; intptr_t buffer_size_; intptr_t encrypted_buffer_size_; Dart_PersistentHandle string_start_; Dart_PersistentHandle string_length_; Dart_PersistentHandle dart_buffer_objects_[kNumBuffers]; Dart_PersistentHandle handshake_complete_; Dart_PersistentHandle bad_certificate_callback_; bool in_handshake_; bool connected_; bool bad_cert_; bool is_server_; char* hostname_; DISALLOW_COPY_AND_ASSIGN(SSLFilter); }; } // namespace bin } // namespace dart #endif // RUNTIME_BIN_SECURE_SOCKET_MACOS_H_