diff options
author | Adam Midvidy <amidvidy@gmail.com> | 2015-07-22 17:16:00 -0400 |
---|---|---|
committer | Adam Midvidy <amidvidy@gmail.com> | 2015-07-22 17:16:00 -0400 |
commit | c99d98a9a82d86f65c55ff7bf7df04c44d48d5d5 (patch) | |
tree | 266d3331da3fa44d88f3ccb82ea2033e843df863 /src | |
parent | 917290ba9644cbf6d981bc730444a107248d1aa3 (diff) | |
download | mongo-c99d98a9a82d86f65c55ff7bf7df04c44d48d5d5.tar.gz |
Revert "SERVER-19221 implement async SSL in NetworkInterfaceASIO"
This reverts commit 917290ba9644cbf6d981bc730444a107248d1aa3.
Diffstat (limited to 'src')
-rw-r--r-- | src/mongo/base/error_codes.err | 4 | ||||
-rw-r--r-- | src/mongo/db/db.cpp | 7 | ||||
-rw-r--r-- | src/mongo/executor/network_interface_asio.cpp | 15 | ||||
-rw-r--r-- | src/mongo/executor/network_interface_asio.h | 76 | ||||
-rw-r--r-- | src/mongo/executor/network_interface_asio_auth.cpp | 2 | ||||
-rw-r--r-- | src/mongo/executor/network_interface_asio_command.cpp | 49 | ||||
-rw-r--r-- | src/mongo/executor/network_interface_asio_connect.cpp | 109 | ||||
-rw-r--r-- | src/mongo/executor/network_interface_asio_operation.cpp | 65 | ||||
-rw-r--r-- | src/mongo/executor/network_interface_asio_ssl.cpp | 81 | ||||
-rw-r--r-- | src/mongo/util/net/sock.cpp | 4 | ||||
-rw-r--r-- | src/mongo/util/net/ssl_manager.cpp | 151 | ||||
-rw-r--r-- | src/mongo/util/net/ssl_manager.h | 31 | ||||
-rw-r--r-- | src/third_party/asio-asio-1-11-0/SConscript | 12 |
13 files changed, 198 insertions, 408 deletions
diff --git a/src/mongo/base/error_codes.err b/src/mongo/base/error_codes.err index 83a0c6bd81f..951dd09e81e 100644 --- a/src/mongo/base/error_codes.err +++ b/src/mongo/base/error_codes.err @@ -139,8 +139,6 @@ error_code("CappedPositionLost", 136) error_code("IncompatibleShardingConfigVersion", 137) error_code("RemoteOplogStale", 138) error_code("JSInterpreterFailure", 139) -error_code("InvalidSSLConfiguration", 140) -error_code("SSLHandshakeFailed", 141) # Non-sequential error codes (for compatibility only) error_code("NotMaster", 10107) #this comes from assert_util.h @@ -160,5 +158,5 @@ error_code("PrepareConfigsFailedCode", 13104); error_class("NetworkError", ["HostUnreachable", "HostNotFound", "NetworkTimeout"]) error_class("Interruption", ["Interrupted", "InterruptedAtShutdown", "ExceededTimeLimit"]) -error_class("IndexCreationError", ["CannotCreateIndex", "IndexOptionsConflict", +error_class("IndexCreationError", ["CannotCreateIndex", "IndexOptionsConflict", "IndexKeySpecsConflict", "IndexAlreadyExists"]) diff --git a/src/mongo/db/db.cpp b/src/mongo/db/db.cpp index 2b4ab0e3f26..f0d95373b04 100644 --- a/src/mongo/db/db.cpp +++ b/src/mongo/db/db.cpp @@ -731,7 +731,12 @@ static void startupConfigActions(const std::vector<std::string>& args) { #endif } -MONGO_INITIALIZER_WITH_PREREQUISITES(CreateReplicationManager, ("SetGlobalEnvironment")) +MONGO_INITIALIZER_WITH_PREREQUISITES(CreateReplicationManager, +#if MONGO_CONFIG_SSL + ("SetGlobalEnvironment", "SSLManager")) +#else + ("SetGlobalEnvironment")) +#endif (InitializerContext* context) { auto replCoord = stdx::make_unique<repl::ReplicationCoordinatorImpl>( getGlobalReplSettings(), diff --git a/src/mongo/executor/network_interface_asio.cpp b/src/mongo/executor/network_interface_asio.cpp index c190c3aa662..3b6acdd6083 100644 --- a/src/mongo/executor/network_interface_asio.cpp +++ b/src/mongo/executor/network_interface_asio.cpp @@ -34,32 +34,17 @@ #include <utility> -#include "mongo/config.h" #include "mongo/stdx/chrono.h" #include "mongo/stdx/memory.h" #include "mongo/util/log.h" #include "mongo/util/net/sock.h" -#include "mongo/util/net/ssl_manager.h" - namespace mongo { namespace executor { NetworkInterfaceASIO::NetworkInterfaceASIO() : _io_service(), _resolver(_io_service), _state(State::kReady), _isExecutorRunnable(false) { _connPool = stdx::make_unique<ConnectionPool>(kMessagingPortKeepOpen); - -#if MONGO_CONFIG_SSL - if (getSSLManager()) { - // We use sslv23, which corresponds to OpenSSLs SSLv23_method, for compatibility with older - // versions of OpenSSL. This mirrors the call to SSL_CTX_new in ssl_manager.cpp. In - // initAsyncSSLContext we explicitly disable all protocols other than TLSv1, TLSv1.1, - // and TLSv1.2. - _sslContext.emplace(asio::ssl::context::sslv23); - uassertStatusOK( - getSSLManager()->initSSLContext(_sslContext->native_handle(), getSSLGlobalParams())); - } -#endif } std::string NetworkInterfaceASIO::getDiagnosticString() { diff --git a/src/mongo/executor/network_interface_asio.h b/src/mongo/executor/network_interface_asio.h index bc1a4a81e5a..0f82c2c618d 100644 --- a/src/mongo/executor/network_interface_asio.h +++ b/src/mongo/executor/network_interface_asio.h @@ -28,20 +28,13 @@ #pragma once -#include "mongo/config.h" - #include <asio.hpp> - #include <boost/optional.hpp> #include <memory> #include <string> #include <system_error> #include <unordered_map> -#ifdef MONGO_CONFIG_SSL -#include <asio/ssl.hpp> -#endif - #include "mongo/base/status.h" #include "mongo/client/connection_pool.h" #include "mongo/executor/network_interface.h" @@ -50,7 +43,6 @@ #include "mongo/platform/atomic_word.h" #include "mongo/rpc/protocol.h" #include "mongo/stdx/condition_variable.h" -#include "mongo/stdx/functional.h" #include "mongo/stdx/mutex.h" #include "mongo/stdx/thread.h" #include "mongo/util/net/message.h" @@ -59,30 +51,6 @@ namespace mongo { namespace executor { /** - * A two-way stream supporting asynchronous reads and writes. - */ -class AsyncStreamInterface { - MONGO_DISALLOW_COPYING(AsyncStreamInterface); - -public: - virtual ~AsyncStreamInterface() = default; - - using ConnectHandler = stdx::function<void(std::error_code)>; - - using StreamHandler = stdx::function<void(std::error_code, std::size_t)>; - - virtual void connect(const asio::ip::tcp::resolver::iterator endpoints, - ConnectHandler&& connectHandler) = 0; - - virtual void write(asio::const_buffer buf, StreamHandler&& writeHandler) = 0; - - virtual void read(asio::mutable_buffer buf, StreamHandler&& readHandler) = 0; - -protected: - AsyncStreamInterface() = default; -}; - -/** * Implementation of the replication system's network interface using Christopher * Kohlhoff's ASIO library instead of existing MongoDB networking primitives. */ @@ -112,21 +80,18 @@ private: enum class State { kReady, kRunning, kShutdown }; - class AsyncStream; - class AsyncSecureStream; - /** * AsyncConnection encapsulates the per-connection state we maintain. */ class AsyncConnection { public: - AsyncConnection(std::unique_ptr<AsyncStreamInterface>, rpc::ProtocolSet serverProtocols); + AsyncConnection(asio::ip::tcp::socket&& sock, rpc::ProtocolSet serverProtocols); - AsyncConnection(std::unique_ptr<AsyncStreamInterface>, + AsyncConnection(asio::ip::tcp::socket&& sock, rpc::ProtocolSet serverProtocols, boost::optional<ConnectionPool::ConnectionPtr>&& bootstrapConn); - AsyncStreamInterface& stream(); + asio::ip::tcp::socket& sock(); rpc::ProtocolSet serverProtocols() const; rpc::ProtocolSet clientProtocols() const; @@ -142,7 +107,7 @@ private: #endif private: - std::unique_ptr<AsyncStreamInterface> _stream; + asio::ip::tcp::socket _sock; rpc::ProtocolSet _serverProtocols; rpc::ProtocolSet _clientProtocols{rpc::supports::kAll}; @@ -194,14 +159,18 @@ private: const RemoteCommandCompletionFn& onFinish, Date_t now); + std::string toString() const; + void cancel(); bool canceled() const; const TaskExecutor::CallbackHandle& cbHandle() const; - AsyncConnection& connection(); + AsyncConnection* connection(); + void connect(ConnectionPool* const pool, asio::io_service* service, Date_t now); void setConnection(AsyncConnection&& conn); + bool connected() const; // AsyncOp may run multiple commands over its lifetime (for example, an ismaster // command, the command provided to the NetworkInterface via startCommand(), etc.) @@ -220,6 +189,14 @@ private: void setOperationProtocol(rpc::Protocol proto); private: + enum class OpState { + kReady, + kConnectionAcquired, + kConnectionVerified, + kConnected, + kCompleted + }; + // Information describing a task enqueued on the NetworkInterface // via a call to startCommand(). TaskExecutor::CallbackHandle _cbHandle; @@ -240,6 +217,7 @@ private: const Date_t _start; + OpState _state; AtomicUInt64 _canceled; /** @@ -277,17 +255,10 @@ private: // Connection void _connectASIO(AsyncOp* op); void _connectWithDBClientConnection(AsyncOp* op); - - // setup plaintext TCP socket - void _setupSocket(AsyncOp* op, const asio::ip::tcp::resolver::iterator endpoints); - -#ifdef MONGO_CONFIG_SSL - // setup SSL socket - void _setupSecureSocket(AsyncOp* op, asio::ip::tcp::resolver::iterator endpoints); -#endif - + void _setupSocket(AsyncOp* op, const asio::ip::tcp::resolver::iterator& endpoints); void _runIsMaster(AsyncOp* op); void _authenticate(AsyncOp* op); + void _sslHandshake(AsyncOp* op); // Communication state machine void _beginCommunication(AsyncOp* op); @@ -314,13 +285,6 @@ private: stdx::condition_variable _isExecutorRunnableCondition; std::unique_ptr<ConnectionPool> _connPool; - -#ifdef MONGO_CONFIG_SSL - // The SSL context. This declaration is wrapped in an ifdef because the asio::ssl::context - // type does not exist unless SSL support is compiled in. We also use a boost::optional as - // even if SSL support is compiled in, it can be disabled at runtime. - boost::optional<asio::ssl::context> _sslContext; -#endif }; } // namespace executor diff --git a/src/mongo/executor/network_interface_asio_auth.cpp b/src/mongo/executor/network_interface_asio_auth.cpp index d3e72bf956a..176d435fa6b 100644 --- a/src/mongo/executor/network_interface_asio_auth.cpp +++ b/src/mongo/executor/network_interface_asio_auth.cpp @@ -61,7 +61,7 @@ void NetworkInterfaceASIO::_runIsMaster(AsyncOp* op) { if (!protocolSet.isOK()) return _completeOperation(op, protocolSet.getStatus()); - op->connection().setServerProtocols(protocolSet.getValue()); + op->connection()->setServerProtocols(protocolSet.getValue()); // Advance the state machine _authenticate(op); diff --git a/src/mongo/executor/network_interface_asio_command.cpp b/src/mongo/executor/network_interface_asio_command.cpp index 28560bd4f5f..e19b8e6224c 100644 --- a/src/mongo/executor/network_interface_asio_command.cpp +++ b/src/mongo/executor/network_interface_asio_command.cpp @@ -32,7 +32,6 @@ #include "mongo/executor/network_interface_asio.h" -#include <type_traits> #include <utility> #include "mongo/db/dbmessage.h" @@ -54,43 +53,25 @@ namespace executor { namespace { using asio::ip::tcp; +using NetworkOpHandler = stdx::function<void(std::error_code, size_t)>; -// A type conforms to the NetworkHandler concept if it is a callable type that takes a -// std::error_code and std::size_t and returns void. The std::error_code parameter is used -// to inform the handler if the asynchronous operation it was waiting on succeeded, and the size_t -// parameter conveys how many bytes were read or written. -template <typename FunctionLike> -using IsNetworkHandler = - std::is_convertible<FunctionLike, stdx::function<void(std::error_code, std::size_t)>>; - -template <typename Handler> -void asyncSendMessage(AsyncStreamInterface& stream, Message* m, Handler&& handler) { - static_assert(IsNetworkHandler<Handler>::value, - "Handler passed to asyncSendMessage does not conform to NetworkHandler concept"); +// TODO: Consider templatizing on handler here to avoid using stdx::functions. +void asyncSendMessage(tcp::socket& sock, Message* m, NetworkOpHandler handler) { // TODO: Some day we may need to support vector messages. fassert(28708, m->buf() != 0); - stream.write(asio::buffer(m->buf(), m->size()), std::forward<Handler>(handler)); + asio::const_buffer buf(m->buf(), m->size()); + asio::async_write(sock, asio::buffer(buf), handler); } -template <typename Handler> -void asyncRecvMessageHeader(AsyncStreamInterface& stream, - MSGHEADER::Value* header, - Handler&& handler) { - static_assert( - IsNetworkHandler<Handler>::value, - "Handler passed to asyncRecvMessageHeader does not conform to NetworkHandler concept"); - stream.read(asio::buffer(header->view().view2ptr(), sizeof(decltype(*header))), - std::forward<Handler>(handler)); +void asyncRecvMessageHeader(tcp::socket& sock, MSGHEADER::Value* header, NetworkOpHandler handler) { + asio::async_read( + sock, asio::buffer(header->view().view2ptr(), sizeof(MSGHEADER::Value)), handler); } -template <typename Handler> -void asyncRecvMessageBody(AsyncStreamInterface& stream, +void asyncRecvMessageBody(tcp::socket& sock, MSGHEADER::Value* header, Message* m, - Handler&& handler) { - static_assert( - IsNetworkHandler<Handler>::value, - "Handler passed to asyncRecvMessageBody does not conform to NetworkHandler concept"); + NetworkOpHandler handler) { // TODO: This error code should be more meaningful. std::error_code ec; @@ -118,7 +99,7 @@ void asyncRecvMessageBody(AsyncStreamInterface& stream, invariant(bodyLength >= 0); // receive remaining data into md->data - stream.read(asio::buffer(mdView.data(), bodyLength), std::forward<Handler>(handler)); + asio::async_read(sock, asio::buffer(mdView.data(), bodyLength), handler); } } // namespace @@ -183,7 +164,7 @@ std::unique_ptr<Message> NetworkInterfaceASIO::_messageFromRequest( void NetworkInterfaceASIO::_beginCommunication(AsyncOp* op) { auto negotiatedProtocol = - rpc::negotiate(op->connection().serverProtocols(), op->connection().clientProtocols()); + rpc::negotiate(op->connection()->serverProtocols(), op->connection()->clientProtocols()); if (!negotiatedProtocol.isOK()) { return _completeOperation(op, negotiatedProtocol.getStatus()); @@ -283,7 +264,7 @@ void NetworkInterfaceASIO::_asyncRunCommand(AsyncCommand* cmd, NetworkOpHandler } asyncRecvMessageBody( - cmd->conn().stream(), &cmd->header(), &cmd->toRecv(), std::move(recvMessageCallback)); + cmd->conn().sock(), &cmd->header(), &cmd->toRecv(), std::move(recvMessageCallback)); }; // Step 2 @@ -292,11 +273,11 @@ void NetworkInterfaceASIO::_asyncRunCommand(AsyncCommand* cmd, NetworkOpHandler if (ec) return handler(ec, bytes); - asyncRecvMessageHeader(cmd->conn().stream(), &cmd->header(), std::move(recvHeaderCallback)); + asyncRecvMessageHeader(cmd->conn().sock(), &cmd->header(), std::move(recvHeaderCallback)); }; // Step 1 - asyncSendMessage(cmd->conn().stream(), &cmd->toSend(), std::move(sendMessageCallback)); + asyncSendMessage(cmd->conn().sock(), &cmd->toSend(), std::move(sendMessageCallback)); } } // namespace executor diff --git a/src/mongo/executor/network_interface_asio_connect.cpp b/src/mongo/executor/network_interface_asio_connect.cpp index 2d01a97e773..2ac64ddcf43 100644 --- a/src/mongo/executor/network_interface_asio_connect.cpp +++ b/src/mongo/executor/network_interface_asio_connect.cpp @@ -34,59 +34,23 @@ #include <utility> -#include "mongo/config.h" -#include "mongo/stdx/memory.h" #include "mongo/util/log.h" #include "mongo/util/net/sock.h" -#ifdef MONGO_CONFIG_SSL -#include "mongo/util/net/ssl_manager.h" -#include "mongo/util/net/ssl_options.h" -#endif - namespace mongo { namespace executor { using asio::ip::tcp; -class NetworkInterfaceASIO::AsyncStream final : public AsyncStreamInterface { -public: - // TODO: after we get rid of the bootstrap connection path, change this constructor - // to take the io_service instead to more closely match AsyncSecureStream - AsyncStream(tcp::socket&& stream) : _stream(std::move(stream)) {} - - void connect(const tcp::resolver::iterator iter, ConnectHandler&& connectHandler) override { - asio::async_connect( - _stream, - std::move(iter), - // We need to wrap this with a lambda of the right signature so it compiles, even - // if we don't actually use the resolver iterator. - [this, connectHandler](std::error_code ec, tcp::resolver::iterator) { - return connectHandler(ec); - }); - } - - void write(asio::const_buffer buffer, StreamHandler&& streamHandler) override { - asio::async_write(_stream, asio::buffer(buffer), std::move(streamHandler)); - } - - void read(asio::mutable_buffer buffer, StreamHandler&& streamHandler) override { - asio::async_read(_stream, asio::buffer(buffer), std::move(streamHandler)); - } - -private: - tcp::socket _stream; -}; - -NetworkInterfaceASIO::AsyncConnection::AsyncConnection(std::unique_ptr<AsyncStreamInterface> stream, +NetworkInterfaceASIO::AsyncConnection::AsyncConnection(asio::ip::tcp::socket&& sock, rpc::ProtocolSet protocols) - : AsyncConnection(std::move(stream), protocols, boost::none) {} + : AsyncConnection(std::move(sock), protocols, boost::none) {} NetworkInterfaceASIO::AsyncConnection::AsyncConnection( - std::unique_ptr<AsyncStreamInterface> stream, + asio::ip::tcp::socket&& sock, rpc::ProtocolSet protocols, boost::optional<ConnectionPool::ConnectionPtr>&& bootstrapConn) - : _stream(std::move(stream)), + : _sock(std::move(sock)), _serverProtocols(protocols), _bootstrapConn(std::move(bootstrapConn)) {} @@ -105,8 +69,8 @@ NetworkInterfaceASIO::AsyncConnection& NetworkInterfaceASIO::AsyncConnection::op } #endif -AsyncStreamInterface& NetworkInterfaceASIO::AsyncConnection::stream() { - return *_stream; +asio::ip::tcp::socket& NetworkInterfaceASIO::AsyncConnection::sock() { + return _sock; } rpc::ProtocolSet NetworkInterfaceASIO::AsyncConnection::serverProtocols() const { @@ -125,24 +89,11 @@ void NetworkInterfaceASIO::_connectASIO(AsyncOp* op) { tcp::resolver::query query(op->request().target.host(), std::to_string(op->request().target.port())); // TODO: Investigate how we might hint or use shortcuts to resolve when possible. - const auto thenConnect = [this, op](std::error_code ec, tcp::resolver::iterator endpoints) { - _validateAndRun(op, - ec, - [this, op, endpoints]() { - -#ifdef MONGO_CONFIG_SSL - int sslModeVal = getSSLGlobalParams().sslMode.load(); - if (sslModeVal == SSLParams::SSLMode_preferSSL || - sslModeVal == SSLParams::SSLMode_requireSSL) { - invariant(_sslContext.is_initialized()); - return _setupSecureSocket(op, std::move(endpoints)); - } -#endif - _setupSocket(op, std::move(endpoints)); - - }); - }; - _resolver.async_resolve(query, std::move(thenConnect)); + _resolver.async_resolve( + query, + [this, op](std::error_code ec, asio::ip::basic_resolver_iterator<tcp> endpoints) { + _validateAndRun(op, ec, [this, op, endpoints]() { _setupSocket(op, endpoints); }); + }); } void NetworkInterfaceASIO::_connectWithDBClientConnection(AsyncOp* op) { @@ -153,24 +104,7 @@ void NetworkInterfaceASIO::_connectWithDBClientConnection(AsyncOp* op) { // - we cannot get a new connection from the pool // - we get a connection from the pool, but cannot use it // - we fail to transfer the connection's socket to an ASIO wrapper - // TODO(amidvidy): why is this hardcoded to 1 second? That seems too low. - ConnectionPool::ConnectionPtr conn( - _connPool.get(), op->request().target, now(), Milliseconds(1000)); - - // TODO: Add a case here for unix domain sockets. - int protocol = conn.get()->port().localAddr().getType(); - if (protocol != AF_INET && protocol != AF_INET6) { - throw SocketException(SocketException::CONNECT_ERROR, "Unsupported family"); - } - - tcp::socket sock{_io_service, - protocol == AF_INET ? tcp::v4() : tcp::v6(), - conn.get()->port().psock->rawFD()}; - - op->setConnection(AsyncConnection(stdx::make_unique<AsyncStream>(std::move(sock)), - conn.get()->getServerRPCProtocols(), - std::move(conn))); - + op->connect(_connPool.get(), &_io_service, now()); } catch (...) { LOG(3) << "failed to connect, posting mock completion"; @@ -192,17 +126,18 @@ void NetworkInterfaceASIO::_connectWithDBClientConnection(AsyncOp* op) { t.detach(); } -void NetworkInterfaceASIO::_setupSocket(AsyncOp* op, const tcp::resolver::iterator endpoints) { - // TODO: Consider moving this call to post-auth so we only assign completed connections. - op->setConnection(AsyncConnection(stdx::make_unique<AsyncStream>(tcp::socket{_io_service}), - rpc::supports::kOpQueryOnly)); +void NetworkInterfaceASIO::_setupSocket(AsyncOp* op, const tcp::resolver::iterator& endpoints) { + tcp::socket sock(_io_service); + AsyncConnection conn(std::move(sock), rpc::supports::kOpQueryOnly); - auto& stream = op->connection().stream(); + // TODO: Consider moving this call to post-auth so we only assign completed connections. + op->setConnection(std::move(conn)); - stream.connect(std::move(endpoints), - [this, op](std::error_code ec) { - _validateAndRun(op, ec, [this, op]() { _authenticate(op); }); - }); + asio::async_connect(op->connection()->sock(), + std::move(endpoints), + [this, op](std::error_code ec, tcp::resolver::iterator iter) { + _validateAndRun(op, ec, [this, op]() { _sslHandshake(op); }); + }); } } // namespace executor diff --git a/src/mongo/executor/network_interface_asio_operation.cpp b/src/mongo/executor/network_interface_asio_operation.cpp index 2e7df88a172..82038fc56f3 100644 --- a/src/mongo/executor/network_interface_asio_operation.cpp +++ b/src/mongo/executor/network_interface_asio_operation.cpp @@ -41,7 +41,33 @@ NetworkInterfaceASIO::AsyncOp::AsyncOp(const TaskExecutor::CallbackHandle& cbHan const RemoteCommandRequest& request, const RemoteCommandCompletionFn& onFinish, Date_t now) - : _cbHandle(cbHandle), _request(request), _onFinish(onFinish), _start(now), _canceled(0) {} + : _cbHandle(cbHandle), + _request(request), + _onFinish(onFinish), + _start(now), + _state(OpState::kReady), + _canceled(0) {} + +std::string NetworkInterfaceASIO::AsyncOp::toString() const { + str::stream output; + output << "Operation state: "; + if (_state == OpState::kReady) { + output << "kReady"; + } else if (_state == OpState::kConnectionAcquired) { + output << "kConnectionAcquired"; + } else if (_state == OpState::kConnectionVerified) { + output << "kConnectionVerified"; + } else if (_state == OpState::kConnected) { + output << "kConnected"; + } else if (_state == OpState::kCompleted) { + output << "kCompleted"; + } else { + MONGO_UNREACHABLE; + } + + output << "\n"; + return output; +} void NetworkInterfaceASIO::AsyncOp::cancel() { // An operation may be in mid-flight when it is canceled, so we @@ -57,14 +83,46 @@ const TaskExecutor::CallbackHandle& NetworkInterfaceASIO::AsyncOp::cbHandle() co return _cbHandle; } -NetworkInterfaceASIO::AsyncConnection& NetworkInterfaceASIO::AsyncOp::connection() { +void NetworkInterfaceASIO::AsyncOp::connect(ConnectionPool* const pool, + asio::io_service* service, + Date_t now) { + // TODO(amidvidy): why is this hardcoded to 1 second? That seems too low. + ConnectionPool::ConnectionPtr conn(pool, _request.target, now, Milliseconds(1000)); + + _state = OpState::kConnectionAcquired; + + // TODO: Add a case here for unix domain sockets. + int protocol = conn.get()->port().localAddr().getType(); + if (protocol != AF_INET && protocol != AF_INET6) { + throw SocketException(SocketException::CONNECT_ERROR, "Unsupported family"); + } + + _state = OpState::kConnectionVerified; + + tcp::socket sock{ + *service, protocol == AF_INET ? tcp::v4() : tcp::v6(), conn.get()->port().psock->rawFD()}; + + _connection.emplace(std::move(sock), conn.get()->getServerRPCProtocols(), std::move(conn)); + + _state = OpState::kConnected; +} + +NetworkInterfaceASIO::AsyncConnection* NetworkInterfaceASIO::AsyncOp::connection() { invariant(_connection.is_initialized()); - return *_connection; + return _connection.get_ptr(); } void NetworkInterfaceASIO::AsyncOp::setConnection(AsyncConnection&& conn) { invariant(!_connection.is_initialized()); _connection = std::move(conn); + _state = OpState::kConnected; +} + +bool NetworkInterfaceASIO::AsyncOp::connected() const { + return (_state == OpState::kConnected || + // NOTE: if we fail at kConnectionVerified, + // ASIO will have closed the socket, don't disconnect + _state == OpState::kConnectionAcquired); } NetworkInterfaceASIO::AsyncCommand& NetworkInterfaceASIO::AsyncOp::beginCommand( @@ -89,6 +147,7 @@ NetworkInterfaceASIO::AsyncCommand& NetworkInterfaceASIO::AsyncOp::command() { void NetworkInterfaceASIO::AsyncOp::finish(const ResponseStatus& status) { _onFinish(status); + _state = OpState::kCompleted; } const RemoteCommandRequest& NetworkInterfaceASIO::AsyncOp::request() const { diff --git a/src/mongo/executor/network_interface_asio_ssl.cpp b/src/mongo/executor/network_interface_asio_ssl.cpp index 220885f0089..d61cb6ce156 100644 --- a/src/mongo/executor/network_interface_asio_ssl.cpp +++ b/src/mongo/executor/network_interface_asio_ssl.cpp @@ -28,92 +28,19 @@ #define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kExecutor -#include "mongo/config.h" - -#ifdef MONGO_CONFIG_SSL - #include "mongo/platform/basic.h" #include "mongo/executor/network_interface_asio.h" -#include "mongo/stdx/functional.h" -#include "mongo/stdx/memory.h" -#include "mongo/util/log.h" -#include "mongo/util/net/ssl_manager.h" - namespace mongo { namespace executor { -class NetworkInterfaceASIO::AsyncSecureStream final : public AsyncStreamInterface { -public: - AsyncSecureStream(asio::io_service* io_service, asio::ssl::context* sslContext) - : _stream(*io_service, *sslContext) {} - - void connect(const asio::ip::tcp::resolver::iterator endpoints, - ConnectHandler&& connectHandler) override { - // Stash the connectHandler as we won't be able to call it until we re-enter the state - // machine. - _userHandler = std::move(connectHandler); - asio::async_connect(_stream.lowest_layer(), - std::move(endpoints), - [this](std::error_code ec, asio::ip::tcp::resolver::iterator iter) { - if (ec) { - return _userHandler(ec); - } - return _handleConnect(ec, std::move(iter)); - }); - } - - void write(asio::const_buffer buffer, StreamHandler&& streamHandler) override { - asio::async_write(_stream, asio::buffer(buffer), std::move(streamHandler)); - } - - void read(asio::mutable_buffer buffer, StreamHandler&& streamHandler) override { - asio::async_read(_stream, asio::buffer(buffer), std::move(streamHandler)); - } +void NetworkInterfaceASIO::_sslHandshake(AsyncOp* op) { + // TODO: Implement asynchronous SSL, SERVER-19221 -private: - void _handleConnect(std::error_code ec, asio::ip::tcp::resolver::iterator iter) { - _stream.async_handshake(decltype(_stream)::client, - [this, iter](std::error_code ec) { - if (ec) { - return _userHandler(ec); - } - return _handleHandshake(ec, iter->host_name()); - }); - } - - void _handleHandshake(std::error_code ec, const std::string& hostName) { - auto certStatus = - getSSLManager()->parseAndValidatePeerCertificate(_stream.native_handle(), hostName); - if (!certStatus.isOK()) { - warning() << certStatus.getStatus(); - return _userHandler( - // TODO: fix handling of std::error_code w.r.t. codes used by Status - std::error_code(certStatus.getStatus().code(), std::generic_category())); - } - _userHandler(std::error_code()); - } - - asio::ssl::stream<asio::ip::tcp::socket> _stream; - ConnectHandler _userHandler; -}; - -void NetworkInterfaceASIO::_setupSecureSocket(AsyncOp* op, - asio::ip::tcp::resolver::iterator endpoints) { - auto secureStream = stdx::make_unique<AsyncSecureStream>(&_io_service, _sslContext.get_ptr()); - - // See TODO in _setupSocket for how this call may change. - op->setConnection(AsyncConnection(std::move(secureStream), rpc::supports::kOpQueryOnly)); - - auto& stream = op->connection().stream(); - stream.connect(std::move(endpoints), - [this, op](std::error_code ec) { - _validateAndRun(op, ec, [this, op] { _authenticate(op); }); - }); + // Advance the state machine + _runIsMaster(op); } } // namespace executor } // namespace mongo - -#endif diff --git a/src/mongo/util/net/sock.cpp b/src/mongo/util/net/sock.cpp index 4c34ced371a..d9deed4036e 100644 --- a/src/mongo/util/net/sock.cpp +++ b/src/mongo/util/net/sock.cpp @@ -516,7 +516,7 @@ bool Socket::secure(SSLManagerInterface* mgr, const std::string& remoteHost) { } _sslManager = mgr; _sslConnection.reset(_sslManager->connect(this)); - mgr->parseAndValidatePeerCertificateDeprecated(_sslConnection.get(), remoteHost); + mgr->parseAndValidatePeerCertificate(_sslConnection.get(), remoteHost); return true; } @@ -534,7 +534,7 @@ std::string Socket::doSSLHandshake(const char* firstBytes, int len) { remoteString()); } _sslConnection.reset(_sslManager->accept(this, firstBytes, len)); - return _sslManager->parseAndValidatePeerCertificateDeprecated(_sslConnection.get(), ""); + return _sslManager->parseAndValidatePeerCertificate(_sslConnection.get(), ""); } #endif diff --git a/src/mongo/util/net/ssl_manager.cpp b/src/mongo/util/net/ssl_manager.cpp index 3f6a42745fb..92a67e13b65 100644 --- a/src/mongo/util/net/ssl_manager.cpp +++ b/src/mongo/util/net/ssl_manager.cpp @@ -65,10 +65,6 @@ namespace mongo { SSLParams sslGlobalParams; -const SSLParams& getSSLGlobalParams() { - return sslGlobalParams; -} - #ifdef MONGO_CONFIG_SSL // Old copies of OpenSSL will not have constants to disable protocols they don't support. // Define them to values we can OR together safely to generically disable these protocols across @@ -174,21 +170,12 @@ class SSLManager : public SSLManagerInterface { public: explicit SSLManager(const SSLParams& params, bool isServer); - /** - * Initializes an OpenSSL context according to the provided settings. Only settings which are - * acceptable on non-blocking connections are set. - */ - Status initSSLContext(SSL_CTX* context, const SSLParams& params) final; - virtual SSLConnection* connect(Socket* socket); virtual SSLConnection* accept(Socket* socket, const char* initialBytes, int len); - virtual std::string parseAndValidatePeerCertificateDeprecated(const SSLConnection* conn, - const std::string& remoteHost); - - StatusWith<boost::optional<std::string>> parseAndValidatePeerCertificate( - SSL* conn, const std::string& remoteHost) final; + virtual std::string parseAndValidatePeerCertificate(const SSLConnection* conn, + const std::string& remoteHost); virtual void cleanupThreadLocals(); @@ -232,10 +219,9 @@ private: MONGO_COMPILER_NORETURN void _handleSSLError(int code, int ret); /* - * Init the SSL context using parameters provided in params. This SSL context will - * be configured for blocking send/receive. + * Init the SSL context using parameters provided in params. */ - bool _initSynchronousSSLContext(UniqueSSLContext* context, const SSLParams& params); + bool _initSSLContext(UniqueSSLContext* context, const SSLParams& params); /* * Converts time from OpenSSL return value to unsigned long long @@ -337,7 +323,7 @@ MONGO_INITIALIZER(SetupOpenSSL)(InitializerContext*) { return Status::OK(); } -MONGO_INITIALIZER_GENERAL(SSLManager, ("SetupOpenSSL"), ("CreateReplicationManager")) +MONGO_INITIALIZER_WITH_PREREQUISITES(SSLManager, ("SetupOpenSSL")) (InitializerContext*) { stdx::lock_guard<SimpleMutex> lck(sslManagerMtx); if (sslGlobalParams.sslMode.load() != SSLParams::SSLMode_disabled) { @@ -426,7 +412,7 @@ SSLManager::SSLManager(const SSLParams& params, bool isServer) _weakValidation(params.sslWeakCertificateValidation), _allowInvalidCertificates(params.sslAllowInvalidCertificates), _allowInvalidHostnames(params.sslAllowInvalidHostnames) { - if (!_initSynchronousSSLContext(&_clientContext, params)) { + if (!_initSSLContext(&_clientContext, params)) { uasserted(16768, "ssl initialization problem"); } @@ -448,7 +434,7 @@ SSLManager::SSLManager(const SSLParams& params, bool isServer) } // SSL server specific initialization if (isServer) { - if (!_initSynchronousSSLContext(&_serverContext, params)) { + if (!_initSSLContext(&_serverContext, params)) { uasserted(16562, "ssl initialization problem"); } @@ -525,7 +511,13 @@ void SSLManager::SSL_free(SSLConnection* conn) { return ::SSL_free(conn->ssl); } -Status SSLManager::initSSLContext(SSL_CTX* context, const SSLParams& params) { +bool SSLManager::_initSSLContext(UniqueSSLContext* contextPtr, const SSLParams& params) { + UniqueSSLContext context(SSL_CTX_new(SSLv23_method()), _free_ssl_context); + massert(15864, + mongoutils::str::stream() + << "can't create SSL Context: " << getSSLErrorMessage(ERR_get_error()), + context); + // SSL_OP_ALL - Activate all bug workaround options, to support buggy client SSL's. // SSL_OP_NO_SSLv2 - Disable SSL v2 support // SSL_OP_NO_SSLv3 - Disable SSL v3 support @@ -543,7 +535,7 @@ Status SSLManager::initSSLContext(SSL_CTX* context, const SSLParams& params) { } } } - ::SSL_CTX_set_options(context, supportedProtocols); + SSL_CTX_set_options(context.get(), supportedProtocols); // HIGH - Enable strong ciphers // !EXPORT - Disable export ciphers (40/56 bit) @@ -556,58 +548,51 @@ Status SSLManager::initSSLContext(SSL_CTX* context, const SSLParams& params) { cipherConfig = params.sslCipherConfig; } - if (0 == ::SSL_CTX_set_cipher_list(context, cipherConfig.c_str())) { - return Status(ErrorCodes::InvalidSSLConfiguration, - str::stream() << "Can not set supported cipher suites: " - << getSSLErrorMessage(ERR_get_error())); - } + massert(28615, + mongoutils::str::stream() + << "can't set supported cipher suites: " << getSSLErrorMessage(ERR_get_error()), + SSL_CTX_set_cipher_list(context.get(), cipherConfig.c_str())); - // We use the address of the context as the session id context. - if (0 == ::SSL_CTX_set_session_id_context( - context, reinterpret_cast<unsigned char*>(&context), sizeof(context))) { - return Status(ErrorCodes::InvalidSSLConfiguration, - str::stream() << "Can not store ssl session id context: " - << getSSLErrorMessage(ERR_get_error())); - } + // If renegotiation is needed, don't return from recv() or send() until it's successful. + // Note: this is for blocking sockets only. + SSL_CTX_set_mode(context.get(), SSL_MODE_AUTO_RETRY); - if (!params.sslClusterFile.empty()) { - ::EVP_set_pw_prompt("Enter cluster certificate passphrase"); - if (!_setupPEM(context, params.sslClusterFile, params.sslClusterPassword)) { - return Status(ErrorCodes::InvalidSSLConfiguration, "Can not set up ssl clusterFile."); + massert(28607, + mongoutils::str::stream() + << "can't store ssl session id context: " << getSSLErrorMessage(ERR_get_error()), + SSL_CTX_set_session_id_context( + context.get(), + static_cast<unsigned char*>(static_cast<void*>(contextPtr)), + sizeof(*contextPtr))); + + // Use the clusterfile for internal outgoing SSL connections if specified + if (contextPtr == &_clientContext && !params.sslClusterFile.empty()) { + EVP_set_pw_prompt("Enter cluster certificate passphrase"); + if (!_setupPEM(context.get(), params.sslClusterFile, params.sslClusterPassword)) { + return false; } - } else if (!params.sslPEMKeyFile.empty()) { - // Use the pemfile for everything else - ::EVP_set_pw_prompt("Enter PEM passphrase"); - if (!_setupPEM(context, params.sslPEMKeyFile, params.sslPEMKeyPassword)) { - return Status(ErrorCodes::InvalidSSLConfiguration, "Can not set up PEM key file."); + } + // Use the pemfile for everything else + else if (!params.sslPEMKeyFile.empty()) { + EVP_set_pw_prompt("Enter PEM passphrase"); + if (!_setupPEM(context.get(), params.sslPEMKeyFile, params.sslPEMKeyPassword)) { + return false; } } if (!params.sslCAFile.empty()) { // Set up certificate validation with a certificate authority - if (!_setupCA(context, params.sslCAFile)) { - return Status(ErrorCodes::InvalidSSLConfiguration, "Can not set up CA file."); + if (!_setupCA(context.get(), params.sslCAFile)) { + return false; } } if (!params.sslCRLFile.empty()) { - if (!_setupCRL(context, params.sslCRLFile)) { - return Status(ErrorCodes::InvalidSSLConfiguration, "Can not set up CRL file."); + if (!_setupCRL(context.get(), params.sslCRLFile)) { + return false; } } - return Status::OK(); -} - -bool SSLManager::_initSynchronousSSLContext(UniqueSSLContext* contextPtr, const SSLParams& params) { - UniqueSSLContext context(SSL_CTX_new(SSLv23_method()), _free_ssl_context); - - uassertStatusOK(initSSLContext(context.get(), params)); - - // If renegotiation is needed, don't return from recv() or send() until it's successful. - // Note: this is for blocking sockets only. - SSL_CTX_set_mode(context.get(), SSL_MODE_AUTO_RETRY); - *contextPtr = std::move(context); return true; } @@ -882,38 +867,35 @@ bool SSLManager::_hostNameMatch(const char* nameToMatch, const char* certHostNam } } -StatusWith<boost::optional<std::string>> SSLManager::parseAndValidatePeerCertificate( - SSL* conn, const std::string& remoteHost) { +std::string SSLManager::parseAndValidatePeerCertificate(const SSLConnection* conn, + const std::string& remoteHost) { // only set if a CA cert has been provided if (!_sslConfiguration.hasCA) - return {boost::none}; + return ""; - X509* peerCert = SSL_get_peer_certificate(conn); + X509* peerCert = SSL_get_peer_certificate(conn->ssl); if (NULL == peerCert) { // no certificate presented by peer if (_weakValidation) { warning() << "no SSL certificate provided by peer" << endl; } else { - auto msg = "no SSL certificate provided by peer; connection rejected"; - error() << msg; - return Status(ErrorCodes::SSLHandshakeFailed, msg); + error() << "no SSL certificate provided by peer; connection rejected" << endl; + throw SocketException(SocketException::CONNECT_ERROR, ""); } - return {boost::none}; + return ""; } ON_BLOCK_EXIT(X509_free, peerCert); - long result = SSL_get_verify_result(conn); + long result = SSL_get_verify_result(conn->ssl); if (result != X509_V_OK) { if (_allowInvalidCertificates) { warning() << "SSL peer certificate validation failed:" << X509_verify_cert_error_string(result); } else { - str::stream msg; - msg << "SSL peer certificate validation failed:" - << X509_verify_cert_error_string(result); - error() << msg.ss.str(); - return Status(ErrorCodes::SSLHandshakeFailed, msg); + error() << "SSL peer certificate validation failed:" + << X509_verify_cert_error_string(result); + throw SocketException(SocketException::CONNECT_ERROR, ""); } } @@ -923,7 +905,7 @@ StatusWith<boost::optional<std::string>> SSLManager::parseAndValidatePeerCertifi // If this is an SSL client context (on a MongoDB server or client) // perform hostname validation of the remote server if (remoteHost.empty()) { - return boost::make_optional(peerSubjectName); + return peerSubjectName; } // Try to match using the Subject Alternate Name, if it exists. @@ -965,25 +947,12 @@ StatusWith<boost::optional<std::string>> SSLManager::parseAndValidatePeerCertifi if (_allowInvalidCertificates || _allowInvalidHostnames) { warning() << "The server certificate does not match the host name " << remoteHost; } else { - str::stream msg; - msg << "The server certificate does not match the host name " << remoteHost; - error() << msg.ss.str(); - return Status(ErrorCodes::SSLHandshakeFailed, msg); + error() << "The server certificate does not match the host name " << remoteHost; + throw SocketException(SocketException::CONNECT_ERROR, ""); } } - return boost::make_optional(peerSubjectName); -} - -std::string SSLManager::parseAndValidatePeerCertificateDeprecated(const SSLConnection* conn, - const std::string& remoteHost) { - auto swPeerSubjectName = parseAndValidatePeerCertificate(conn->ssl, remoteHost); - // We can't use uassertStatusOK here because we need to throw a SocketException. - if (!swPeerSubjectName.isOK()) { - throw SocketException(SocketException::CONNECT_ERROR, - swPeerSubjectName.getStatus().reason()); - } - return swPeerSubjectName.getValue().get_value_or(""); + return peerSubjectName; } void SSLManager::cleanupThreadLocals() { diff --git a/src/mongo/util/net/ssl_manager.h b/src/mongo/util/net/ssl_manager.h index f9bd9b51484..1c6295ed517 100644 --- a/src/mongo/util/net/ssl_manager.h +++ b/src/mongo/util/net/ssl_manager.h @@ -27,7 +27,6 @@ #pragma once -#include <boost/optional.hpp> #include <memory> #include <string> @@ -110,13 +109,9 @@ public: * Fetches a peer certificate and validates it if it exists * Throws SocketException on failure * @return a std::string containing the certificate's subject name. - * - * This version of parseAndValidatePeerCertificate is deprecated because it throws a - * SocketException upon failure. New code should prefer the version that returns - * a StatusWith instead. */ - virtual std::string parseAndValidatePeerCertificateDeprecated( - const SSLConnection* conn, const std::string& remoteHost) = 0; + virtual std::string parseAndValidatePeerCertificate(const SSLConnection* conn, + const std::string& remoteHost) = 0; /** * Cleans up SSL thread local memory; use at thread exit @@ -151,33 +146,11 @@ public: virtual int SSL_shutdown(SSLConnection* conn) = 0; virtual void SSL_free(SSLConnection* conn) = 0; - - /** - * Initializes an OpenSSL context according to the provided settings. Only settings which are - * acceptable on non-blocking connections are set. - */ - virtual Status initSSLContext(SSL_CTX* context, const SSLParams& params) = 0; - - /** - * Fetches a peer certificate and validates it if it exists. If validation fails, but weak - * validation is enabled, boost::none will be returned. If validation fails, and invalid - * certificates are not allowed, a non-OK status will be returned. If validation is successful, - * an engaged optional containing the certificate's subject name will be returned. - */ - virtual StatusWith<boost::optional<std::string>> parseAndValidatePeerCertificate( - SSL* ssl, const std::string& remoteHost) = 0; }; // Access SSL functions through this instance. SSLManagerInterface* getSSLManager(); extern bool isSSLServer; - -/** - * The global SSL configuration. This should be accessed only after global initialization has - * completed. If it must be accessed in an initializer, the initializer should have - * "EndStartupOptionStorage" as a prerequisite. - */ -const SSLParams& getSSLGlobalParams(); } #endif // #ifdef MONGO_CONFIG_SSL diff --git a/src/third_party/asio-asio-1-11-0/SConscript b/src/third_party/asio-asio-1-11-0/SConscript index 242bb7db889..afa03658783 100644 --- a/src/third_party/asio-asio-1-11-0/SConscript +++ b/src/third_party/asio-asio-1-11-0/SConscript @@ -1,14 +1,8 @@ Import("env") -Import("has_option") - -asio_src = [ - "asio/src/asio.cpp", -] - -if has_option("ssl"): - asio_src.append("asio/src/asio_ssl.cpp") env.Library( target="asio", - source=asio_src + source=[ + "asio/src/asio.cpp", + ] ) |