summaryrefslogtreecommitdiff
path: root/src/mongo/util/net/ssl
diff options
context:
space:
mode:
authorSara Golemon <sara.golemon@mongodb.com>2018-02-20 12:39:37 -0500
committerSara Golemon <sara.golemon@mongodb.com>2018-03-17 12:45:29 -0400
commit65191bbe5c00bb419a7466fb7db43e220035f776 (patch)
tree1f2c00082b35eafbcdaa1539ed680ac84949a640 /src/mongo/util/net/ssl
parent9eea82ee2a0e37da90cbb549d55c5eac8aa69a55 (diff)
downloadmongo-65191bbe5c00bb419a7466fb7db43e220035f776.tar.gz
SERVER-22412 Implement a secure transport ASIO backend
Diffstat (limited to 'src/mongo/util/net/ssl')
-rw-r--r--src/mongo/util/net/ssl/apple.hpp98
-rw-r--r--src/mongo/util/net/ssl/context.hpp4
-rw-r--r--src/mongo/util/net/ssl/context_apple.hpp82
-rw-r--r--src/mongo/util/net/ssl/detail/engine.hpp4
-rw-r--r--src/mongo/util/net/ssl/detail/engine_apple.hpp103
-rw-r--r--src/mongo/util/net/ssl/detail/impl/engine_apple.ipp347
-rw-r--r--src/mongo/util/net/ssl/detail/stream_core.hpp4
-rw-r--r--src/mongo/util/net/ssl/impl/error.ipp10
-rw-r--r--src/mongo/util/net/ssl/impl/src.hpp5
-rw-r--r--src/mongo/util/net/ssl/stream.hpp3
10 files changed, 660 insertions, 0 deletions
diff --git a/src/mongo/util/net/ssl/apple.hpp b/src/mongo/util/net/ssl/apple.hpp
new file mode 100644
index 00000000000..e38e2b46c97
--- /dev/null
+++ b/src/mongo/util/net/ssl/apple.hpp
@@ -0,0 +1,98 @@
+/**
+ * Copyright (C) 2018 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#pragma once
+
+#if MONGO_CONFIG_SSL_PROVIDER == SSL_PROVIDER_APPLE
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <Security/Security.h>
+#include <memory>
+#include <string>
+
+namespace asio {
+namespace ssl {
+namespace apple {
+
+namespace {
+template <typename T>
+struct CFReleaser {
+ void operator()(T ptr) {
+ if (ptr) {
+ ::CFRelease(ptr);
+ }
+ }
+};
+} // namespace
+
+/**
+ * CoreFoundation types are internally refcounted using CFRetain/CFRelease.
+ * Values received from a method using the word "Copy" typically follow "The Copy Rule"
+ * which requires that the caller explicitly invoke CFRelease on the obtained value.
+ * Values received from a method using the word "Get" typically follow "The Get Rule"
+ * which requires that the caller DOES NOT attempt to release any references,
+ * though it may invoke CFRetain to hold on to the object for longer.
+ *
+ * Use of the CFUniquePtr type assumes that a value was wither obtained from a "Copy"
+ * method, or that it has been explicitly retained.
+ */
+template <typename T>
+using CFUniquePtr = std::unique_ptr<typename std::remove_pointer<T>::type, CFReleaser<T>>;
+
+/**
+ * Equivalent of OpenSSL's SSL_CTX type.
+ * Allows loading SecIdentity and SecCertificate chains
+ * separate from an SSLContext instance.
+ *
+ * Unlike OpenSSL, Secure Transport sets protocol range on
+ * each connection instance separately, so just stash them aside
+ * in the same place for now.
+ */
+struct Context {
+ Context() = default;
+ explicit Context(::SSLProtocol p) : protoMin(p), protoMax(p) {}
+ Context& operator=(const Context& src) {
+ protoMin = src.protoMin;
+ protoMax = src.protoMax;
+ if (src.certs) {
+ ::CFRetain(src.certs.get());
+ }
+ certs.reset(src.certs.get());
+ return *this;
+ }
+
+ ::SSLProtocol protoMin = kTLSProtocol1;
+ ::SSLProtocol protoMax = kTLSProtocol12;
+ CFUniquePtr<::CFArrayRef> certs;
+};
+
+} // namespace apple
+} // namespace ssl
+} // namespace asio
+
+#endif
diff --git a/src/mongo/util/net/ssl/context.hpp b/src/mongo/util/net/ssl/context.hpp
index 278530f5916..2a23b8454c8 100644
--- a/src/mongo/util/net/ssl/context.hpp
+++ b/src/mongo/util/net/ssl/context.hpp
@@ -34,6 +34,10 @@
#include "mongo/util/net/ssl/context_openssl.hpp"
+#elif MONGO_CONFIG_SSL_PROVIDER == SSL_PROVIDER_APPLE
+
+#include "mongo/util/net/ssl/context_apple.hpp"
+
#else
#error "Unknown SSL Provider"
#endif
diff --git a/src/mongo/util/net/ssl/context_apple.hpp b/src/mongo/util/net/ssl/context_apple.hpp
new file mode 100644
index 00000000000..3fd7f7eabde
--- /dev/null
+++ b/src/mongo/util/net/ssl/context_apple.hpp
@@ -0,0 +1,82 @@
+/**
+ * Copyright 2018 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#pragma once
+
+#include "asio/detail/config.hpp"
+#include "asio/detail/noncopyable.hpp"
+#include "mongo/util/net/ssl/apple.hpp"
+#include "mongo/util/net/ssl/context_base.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace ssl {
+
+class context : public context_base, private noncopyable {
+public:
+ using native_handle_type = apple::Context*;
+
+ ASIO_DECL explicit context(method m) {
+ _context.protoMin = _mapProto(m);
+ _context.protoMax = _context.protoMax;
+ }
+
+ ASIO_DECL context(context&& other) = default;
+ ASIO_DECL context& operator=(context&& other) = default;
+
+ ASIO_DECL native_handle_type native_handle() {
+ return &_context;
+ }
+
+private:
+ static ::SSLProtocol _mapProto(method m) {
+ switch (m) {
+ case context::tlsv1:
+ case context::tlsv1_client:
+ case context::tlsv1_server:
+ return ::kTLSProtocol1;
+ case context::tlsv11:
+ case context::tlsv11_client:
+ case context::tlsv11_server:
+ return ::kTLSProtocol11;
+ case context::tlsv12:
+ case context::tlsv12_client:
+ case context::tlsv12_server:
+ default:
+ return ::kTLSProtocol12;
+ }
+ }
+
+ apple::Context _context;
+};
+
+} // namespace ssl
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
diff --git a/src/mongo/util/net/ssl/detail/engine.hpp b/src/mongo/util/net/ssl/detail/engine.hpp
index 80fc21c5daf..9beb6724b7e 100644
--- a/src/mongo/util/net/ssl/detail/engine.hpp
+++ b/src/mongo/util/net/ssl/detail/engine.hpp
@@ -34,6 +34,10 @@
#include "mongo/util/net/ssl/detail/engine_openssl.hpp"
+#elif MONGO_CONFIG_SSL_PROVIDER == SSL_PROVIDER_APPLE
+
+#include "mongo/util/net/ssl/detail/engine_apple.hpp"
+
#else
#error "Unknown SSL Provider"
#endif
diff --git a/src/mongo/util/net/ssl/detail/engine_apple.hpp b/src/mongo/util/net/ssl/detail/engine_apple.hpp
new file mode 100644
index 00000000000..69b027ceaa2
--- /dev/null
+++ b/src/mongo/util/net/ssl/detail/engine_apple.hpp
@@ -0,0 +1,103 @@
+/**
+ * Copyright 2018 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#pragma once
+
+#include <deque>
+
+#include "asio/buffer.hpp"
+#include "asio/detail/config.hpp"
+#include "mongo/util/net/ssl/apple.hpp"
+#include "mongo/util/net/ssl/context_apple.hpp"
+#include "mongo/util/net/ssl/stream_base.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace ssl {
+namespace detail {
+
+class engine {
+public:
+ using native_handle_type = ::SSLContextRef;
+ enum want {
+ want_input_and_retry = -2,
+ want_output_and_retry = -1,
+ want_nothing = 0,
+ want_output = 1
+ };
+
+ ASIO_DECL explicit engine(context::native_handle_type context);
+
+ ASIO_DECL native_handle_type native_handle() {
+ return _ssl.get();
+ }
+
+ ASIO_DECL want handshake(stream_base::handshake_type type, asio::error_code& ec);
+
+ ASIO_DECL want shutdown(asio::error_code& ec);
+
+ ASIO_DECL want write(const asio::const_buffer& data,
+ asio::error_code& ec,
+ std::size_t& bytes_transferred);
+
+ ASIO_DECL want read(const asio::mutable_buffer& data,
+ asio::error_code& ec,
+ std::size_t& bytes_transferred);
+
+ ASIO_DECL asio::mutable_buffer get_output(const asio::mutable_buffer& data);
+
+ ASIO_DECL asio::const_buffer put_input(const asio::const_buffer& data);
+
+ ASIO_DECL const asio::error_code& map_error_code(asio::error_code& ec) const;
+
+private:
+ engine(const engine&) = delete;
+ engine& operator=(const engine&) = delete;
+ bool _initSSL(stream_base::handshake_type type, asio::error_code& ec);
+ static ::OSStatus read_func(::SSLConnectionRef ctx, void* data, size_t* data_len);
+ static ::OSStatus write_func(::SSLConnectionRef ctx, const void* data, size_t* data_len);
+ want wouldBlock() const;
+
+ apple::CFUniquePtr<native_handle_type> _ssl;
+ apple::CFUniquePtr<::CFArrayRef> _certs;
+ apple::CFUniquePtr<::CFArrayRef> _ca;
+ ::SSLProtocol _protoMin, _protoMax;
+ std::deque<char> _inbuf;
+ std::deque<char> _outbuf;
+};
+
+} // namespace detail
+} // namespace ssl
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#if defined(ASIO_HEADER_ONLY)
+#include "mongo/util/net/ssl/detail/impl/engine_apple.ipp"
+#endif // defined(ASIO_HEADER_ONLY)
diff --git a/src/mongo/util/net/ssl/detail/impl/engine_apple.ipp b/src/mongo/util/net/ssl/detail/impl/engine_apple.ipp
new file mode 100644
index 00000000000..25abd1a70bb
--- /dev/null
+++ b/src/mongo/util/net/ssl/detail/impl/engine_apple.ipp
@@ -0,0 +1,347 @@
+/**
+ * Copyright (C) 2018 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#pragma once
+
+#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kNetwork
+
+#include "asio/detail/config.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include "asio/detail/throw_error.hpp"
+#include "asio/error.hpp"
+
+#include "mongo/util/log.h"
+#include "mongo/util/mongoutils/str.h"
+#include "mongo/util/net/ssl/apple.hpp"
+#include "mongo/util/net/ssl/detail/engine.hpp"
+#include "mongo/util/net/ssl/error.hpp"
+
+namespace asio {
+namespace ssl {
+namespace detail {
+
+namespace {
+
+std::ostringstream& operator<<(std::ostringstream& ss, ::OSStatus status) {
+ apple::CFUniquePtr<::CFStringRef> errstr(::SecCopyErrorMessageString(status, nullptr));
+ if (!errstr) {
+ ss << "Unknown Error: " << static_cast<int>(status);
+ return ss;
+ }
+ const auto len = ::CFStringGetMaximumSizeForEncoding(::CFStringGetLength(errstr.get()),
+ ::kCFStringEncodingUTF8);
+ std::string ret;
+ ret.resize(len + 1);
+ if (!::CFStringGetCString(errstr.get(), &ret[0], len, ::kCFStringEncodingUTF8)) {
+ ss << "Unknown Error: " << static_cast<int>(status);
+ return ss;
+ }
+
+ ret.resize(strlen(ret.c_str()));
+ ss << ret;
+ return ss;
+}
+
+const class osstatus_category : public error_category {
+public:
+ const char* name() const noexcept final {
+ return "Secure.Transport";
+ }
+
+ std::string message(int value) const noexcept final {
+ const auto status = static_cast<::OSStatus>(value);
+ return mongo::str::stream() << "Secure.Transport: " << status;
+ }
+} OSStatus_category;
+
+asio::error_code errorCode(::OSStatus status) {
+ return asio::error_code(static_cast<int>(status), OSStatus_category);
+}
+
+/**
+ * Verify that an SSL session is ready for I/O (state: Connected).
+ * In all other states, asio should be speaking to the socket directly.
+ */
+bool verifyConnected(::SSLContextRef ssl, asio::error_code* ec) {
+ auto state = ::kSSLAborted;
+ auto status = ::SSLGetSessionState(ssl, &state);
+ if (status != ::errSecSuccess) {
+ // Unable to determine session state.
+ *ec = errorCode(status);
+ return false;
+ }
+ switch (state) {
+ case ::kSSLIdle:
+ *ec = asio::error::not_connected;
+ return false;
+ case ::kSSLHandshake:
+ *ec = asio::error::in_progress;
+ return false;
+ case ::kSSLConnected:
+ return true;
+ case ::kSSLClosed:
+ *ec = asio::error::shut_down;
+ return false;
+ case ::kSSLAborted:
+ *ec = asio::error::connection_aborted;
+ return false;
+ default:
+ // Undefined state, call it an internal error.
+ *ec = errorCode(::errSSLInternal);
+ return false;
+ }
+}
+
+} // namespace
+
+engine::engine(context::native_handle_type context) {
+ if (context) {
+ if (context->certs) {
+ ::CFRetain(context->certs.get());
+ _certs.reset(context->certs.get());
+ }
+ _protoMin = context->protoMin;
+ _protoMax = context->protoMax;
+ } else {
+ apple::Context def;
+ _protoMin = def.protoMin;
+ _protoMax = def.protoMax;
+ }
+}
+
+bool engine::_initSSL(stream_base::handshake_type type, asio::error_code& ec) {
+ if (_ssl) {
+ return true;
+ }
+
+ const auto side = (type == stream_base::client) ? ::kSSLClientSide : ::kSSLServerSide;
+ _ssl.reset(::SSLCreateContext(nullptr, side, ::kSSLStreamType));
+ if (!_ssl) {
+ mongo::error() << "Failed allocating SSLContext";
+ ec = errorCode(::errSSLInternal);
+ return false;
+ }
+
+ auto status = ::SSLSetConnection(_ssl.get(), static_cast<void*>(this));
+
+ // TODO: ::SSLSetPeerDomainName()
+
+ if (_certs && (status == ::errSecSuccess)) {
+ status = ::SSLSetCertificate(_ssl.get(), _certs.get());
+ }
+
+ if (status == ::errSecSuccess) {
+ status = ::SSLSetPeerID(_ssl.get(), _ssl.get(), sizeof(native_handle_type));
+ }
+
+ if (status == ::errSecSuccess) {
+ status = ::SSLSetIOFuncs(_ssl.get(), read_func, write_func);
+ }
+
+ if (status == ::errSecSuccess) {
+ status = ::SSLSetProtocolVersionMin(_ssl.get(), _protoMin);
+ }
+
+ if (status == ::errSecSuccess) {
+ status = ::SSLSetProtocolVersionMax(_ssl.get(), _protoMax);
+ }
+
+ if (status == ::errSecSuccess) {
+ status = ::SSLSetClientSideAuthenticate(_ssl.get(), ::kTryAuthenticate);
+ }
+
+ if (status == ::errSecSuccess) {
+ status = ::SSLSetSessionOption(_ssl.get(), ::kSSLSessionOptionBreakOnServerAuth, true);
+ }
+
+ if (status == ::errSecSuccess) {
+ status = ::SSLSetSessionOption(_ssl.get(), ::kSSLSessionOptionBreakOnClientAuth, true);
+ }
+
+ if (status != ::errSecSuccess) {
+ _ssl.reset(nullptr);
+ ec = errorCode(status);
+ return false;
+ }
+
+ return true;
+}
+
+engine::want engine::handshake(stream_base::handshake_type type, asio::error_code& ec) {
+ if (!_initSSL(type, ec)) {
+ // Error happened, ec has been set.
+ return want::want_nothing;
+ }
+
+ // We use BreakOnClientAuth and BreakOnServerAuth above to
+ // convince the OS not to validate the certs for us.
+ // In practice, we'll be validating the peer in ssl_manager_apple.cpp later.
+ // As a side effect, we have to call SSLHandshake up to three times.
+ // Breaking once for client auth, then for server auth, and finally on completion.
+ ::OSStatus status;
+ do {
+ status = ::SSLHandshake(_ssl.get());
+ } while ((status == ::errSSLServerAuthCompleted) || (status == ::errSSLClientAuthCompleted));
+
+ if (status == ::errSSLWouldBlock) {
+ return wouldBlock();
+ }
+
+ if (status != ::errSecSuccess) {
+ _ssl.reset(nullptr);
+ ec = errorCode(status);
+ return want::want_nothing;
+ }
+
+ return _outbuf.size() ? want::want_output : want::want_nothing;
+}
+
+engine::want engine::shutdown(asio::error_code& ec) {
+ if (_ssl) {
+ const auto status = ::SSLClose(_ssl.get());
+ if (status == ::errSSLWouldBlock) {
+ return wouldBlock();
+ }
+ if (status == ::errSecSuccess) {
+ _ssl.reset(nullptr);
+ } else {
+ ec = errorCode(status);
+ }
+ } else {
+ mongo::error() << "SSL connection already shut down";
+ ec = errorCode(::errSSLInternal);
+ }
+ return want::want_nothing;
+}
+
+const asio::error_code& engine::map_error_code(asio::error_code& ec) const {
+ if (ec != asio::error::eof) {
+ return ec;
+ }
+
+ if (_inbuf.size() || _outbuf.size()) {
+ ec = asio::ssl::error::stream_truncated;
+ return ec;
+ }
+
+ invariant(_ssl);
+ auto state = ::kSSLAborted;
+ const auto status = ::SSLGetSessionState(_ssl.get(), &state);
+ if (status != ::errSecSuccess) {
+ ec = errorCode(status);
+ return ec;
+ }
+
+ if (state == ::kSSLConnected) {
+ ec = asio::ssl::error::stream_truncated;
+ return ec;
+ }
+
+ return ec;
+}
+
+engine::want engine::write(const asio::const_buffer& data,
+ asio::error_code& ec,
+ std::size_t& bytes_transferred) {
+ if (!verifyConnected(_ssl.get(), &ec)) {
+ return want::want_nothing;
+ }
+ const auto status = ::SSLWrite(_ssl.get(), data.data(), data.size(), &bytes_transferred);
+ if (status == ::errSSLWouldBlock) {
+ return (bytes_transferred < data.size()) ? want::want_output_and_retry : want::want_nothing;
+ }
+ if (status != ::errSecSuccess) {
+ ec = errorCode(status);
+ }
+ return _outbuf.size() ? want::want_output : want::want_nothing;
+}
+
+asio::mutable_buffer engine::get_output(const asio::mutable_buffer& data) {
+ const auto len = std::min<size_t>(data.size(), _outbuf.size());
+ if (len > 0) {
+ auto* p = const_cast<char*>(static_cast<const char*>(data.data()));
+ std::copy(_outbuf.begin(), _outbuf.begin() + len, p);
+ _outbuf.erase(_outbuf.begin(), _outbuf.begin() + len);
+ }
+
+ return asio::mutable_buffer(data.data(), len);
+}
+
+::OSStatus engine::write_func(::SSLConnectionRef ctx, const void* data, size_t* data_len) {
+ auto* this_ = const_cast<engine*>(static_cast<const engine*>(ctx));
+ const auto* p = static_cast<const char*>(data);
+ this_->_outbuf.insert(this_->_outbuf.end(), p, p + *data_len);
+ return ::errSecSuccess;
+}
+
+engine::want engine::read(const asio::mutable_buffer& data,
+ asio::error_code& ec,
+ std::size_t& bytes_transferred) {
+ if (!verifyConnected(_ssl.get(), &ec)) {
+ return want::want_nothing;
+ }
+ const auto status = ::SSLRead(_ssl.get(), data.data(), data.size(), &bytes_transferred);
+ if ((status != ::errSSLWouldBlock) && (status != ::errSecSuccess)) {
+ ec = errorCode(status);
+ }
+ return bytes_transferred ? want::want_nothing : wouldBlock();
+}
+
+asio::const_buffer engine::put_input(const asio::const_buffer& data) {
+ const auto* p = static_cast<const char*>(data.data());
+ _inbuf.insert(_inbuf.end(), p, p + data.size());
+ return asio::buffer(data + data.size());
+}
+
+::OSStatus engine::read_func(::SSLConnectionRef ctx, void* data, size_t* data_len) {
+ ::OSStatus ret = ::errSecSuccess;
+ auto* this_ = const_cast<engine*>(static_cast<const engine*>(ctx));
+ if (*data_len > this_->_inbuf.size()) {
+ // If we're able to 100% satisfy the read request Secure Transport made,
+ // then we should ultimately signal that the read is incomplete.
+ ret = ::errSSLWouldBlock;
+ }
+ *data_len = std::min<size_t>(*data_len, this_->_inbuf.size());
+ if (*data_len > 0) {
+ std::copy(
+ this_->_inbuf.begin(), this_->_inbuf.begin() + *data_len, static_cast<char*>(data));
+ this_->_inbuf.erase(this_->_inbuf.begin(), this_->_inbuf.begin() + *data_len);
+ }
+ return ret;
+}
+
+engine::want engine::wouldBlock() const {
+ return _outbuf.empty() ? want::want_input_and_retry : want::want_output_and_retry;
+}
+
+#include "asio/detail/pop_options.hpp"
+
+} // namespace detail
+} // namespace ssl
+} // namespace asio
diff --git a/src/mongo/util/net/ssl/detail/stream_core.hpp b/src/mongo/util/net/ssl/detail/stream_core.hpp
index 21841fec2b9..1740a5bda29 100644
--- a/src/mongo/util/net/ssl/detail/stream_core.hpp
+++ b/src/mongo/util/net/ssl/detail/stream_core.hpp
@@ -22,7 +22,9 @@
#else // defined(ASIO_HAS_BOOST_DATE_TIME)
#include "asio/steady_timer.hpp"
#endif // defined(ASIO_HAS_BOOST_DATE_TIME)
+
#include "asio/buffer.hpp"
+#include "mongo/util/net/ssl/apple.hpp"
#include "mongo/util/net/ssl/detail/engine.hpp"
#include "asio/detail/push_options.hpp"
@@ -40,6 +42,8 @@ struct stream_core {
stream_core(SCHANNEL_CRED* context, asio::io_context& io_context)
#elif MONGO_CONFIG_SSL_PROVIDER == SSL_PROVIDER_OPENSSL
stream_core(SSL_CTX* context, asio::io_context& io_context)
+#elif MONGO_CONFIG_SSL_PROVIDER == SSL_PROVIDER_APPLE
+ stream_core(apple::Context* context, asio::io_context& io_context)
#else
#error "Unknown SSL Provider"
#endif
diff --git a/src/mongo/util/net/ssl/impl/error.ipp b/src/mongo/util/net/ssl/impl/error.ipp
index 60b8f0da177..e2c6bea061e 100644
--- a/src/mongo/util/net/ssl/impl/error.ipp
+++ b/src/mongo/util/net/ssl/impl/error.ipp
@@ -15,8 +15,12 @@
#pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+#include <string>
+
+#include "asio/detail/assert.hpp"
#include "asio/detail/config.hpp"
#include "mongo/util/errno_util.h"
+#include "mongo/util/net/ssl/apple.hpp"
#include "mongo/util/net/ssl/error.hpp"
#include "asio/detail/push_options.hpp"
@@ -40,6 +44,12 @@ public:
const char* s = ::ERR_reason_error_string(value);
return s ? s : "asio.ssl error";
}
+#elif MONGO_CONFIG_SSL_PROVIDER == SSL_PROVIDER_APPLE
+ std::string message(int value) const {
+ // engine_apple produces osstatus_errorcategory messages.
+ ASIO_ASSERT(false);
+ return "asio.ssl error";
+ }
#else
#error "Unknown SSL Provider"
#endif
diff --git a/src/mongo/util/net/ssl/impl/src.hpp b/src/mongo/util/net/ssl/impl/src.hpp
index f5bb866efee..5e300c2147e 100644
--- a/src/mongo/util/net/ssl/impl/src.hpp
+++ b/src/mongo/util/net/ssl/impl/src.hpp
@@ -33,6 +33,11 @@
#include "mongo/util/net/ssl/impl/context_openssl.ipp"
#include "mongo/util/net/ssl/impl/error.ipp"
+#elif MONGO_CONFIG_SSL_PROVIDER == SSL_PROVIDER_APPLE
+
+#include "mongo/util/net/ssl/detail/impl/engine_apple.ipp"
+#include "mongo/util/net/ssl/impl/error.ipp"
+
#else
#error "Unknown SSL Provider"
#endif
diff --git a/src/mongo/util/net/ssl/stream.hpp b/src/mongo/util/net/ssl/stream.hpp
index d44bdc4bd08..df933c0fe55 100644
--- a/src/mongo/util/net/ssl/stream.hpp
+++ b/src/mongo/util/net/ssl/stream.hpp
@@ -22,6 +22,7 @@
#include "asio/detail/handler_type_requirements.hpp"
#include "asio/detail/noncopyable.hpp"
#include "asio/detail/type_traits.hpp"
+#include "mongo/util/net/ssl/apple.hpp"
#include "mongo/util/net/ssl/context.hpp"
#include "mongo/util/net/ssl/detail/buffered_handshake_op.hpp"
#include "mongo/util/net/ssl/detail/handshake_op.hpp"
@@ -67,6 +68,8 @@ public:
typedef PCtxtHandle native_handle_type;
#elif MONGO_CONFIG_SSL_PROVIDER == SSL_PROVIDER_OPENSSL
typedef SSL* native_handle_type;
+#elif MONGO_CONFIG_SSL_PROVIDER == SSL_PROVIDER_APPLE
+ typedef ::SSLContextRef native_handle_type;
#else
#error "Unknown SSL Provider"
#endif