diff options
author | Sara Golemon <sara.golemon@mongodb.com> | 2019-09-25 23:47:58 +0000 |
---|---|---|
committer | evergreen <evergreen@mongodb.com> | 2019-09-25 23:47:58 +0000 |
commit | 3fe0e59fcb5b5007eb1861e7dde2b2126ffdbf2e (patch) | |
tree | 53b62f1ecb5dfb750618d842615842c4adb78d4f /src/mongo | |
parent | 4b439bbd7568f4888f0d2898ca9bb93c38e4b90e (diff) | |
download | mongo-3fe0e59fcb5b5007eb1861e7dde2b2126ffdbf2e.tar.gz |
SERVER-43413 Break SetupOpenSSL initializer to separate library
Diffstat (limited to 'src/mongo')
-rw-r--r-- | src/mongo/crypto/SConscript | 1 | ||||
-rw-r--r-- | src/mongo/util/net/SConscript | 12 | ||||
-rw-r--r-- | src/mongo/util/net/openssl_init.cpp | 182 | ||||
-rw-r--r-- | src/mongo/util/net/ssl_manager_openssl.cpp | 133 |
4 files changed, 195 insertions, 133 deletions
diff --git a/src/mongo/crypto/SConscript b/src/mongo/crypto/SConscript index e7c346162d6..8d151e993c6 100644 --- a/src/mongo/crypto/SConscript +++ b/src/mongo/crypto/SConscript @@ -65,6 +65,7 @@ env.Library(target='symmetric_crypto', ], LIBDEPS=[ '$BUILD_DIR/mongo/base/secure_allocator', + '$BUILD_DIR/mongo/util/net/openssl_init' if 'openssl' in env['MONGO_CRYPTO'] else '', '$BUILD_DIR/mongo/util/secure_zero_memory', ], ) diff --git a/src/mongo/util/net/SConscript b/src/mongo/util/net/SConscript index 3f9d2cb2bca..3031cabf263 100644 --- a/src/mongo/util/net/SConscript +++ b/src/mongo/util/net/SConscript @@ -103,6 +103,17 @@ env.Library( ) if not get_option('ssl') == 'off': + if ssl_provider == 'openssl': + env.Library( + target='openssl_init', + source=[ + 'openssl_init.cpp', + ], + LIBDEPS=[ + '$BUILD_DIR/mongo/base', + 'ssl_options', + ], + ); env.Library( target='ssl_manager', source=[ @@ -118,6 +129,7 @@ if not get_option('ssl') == 'off': '$BUILD_DIR/mongo/db/auth/auth', '$BUILD_DIR/third_party/shim_asio', 'network', + 'openssl_init' if ssl_provider == 'openssl' else '', 'socket', 'ssl_options', 'ssl_types', diff --git a/src/mongo/util/net/openssl_init.cpp b/src/mongo/util/net/openssl_init.cpp new file mode 100644 index 00000000000..8f05d3c877a --- /dev/null +++ b/src/mongo/util/net/openssl_init.cpp @@ -0,0 +1,182 @@ +/** + * Copyright (C) 2019-present MongoDB, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the Server Side Public License, version 1, + * as published by MongoDB, Inc. + * + * 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 + * Server Side Public License for more details. + * + * You should have received a copy of the Server Side Public License + * along with this program. If not, see + * <http://www.mongodb.com/licensing/server-side-public-license>. + * + * 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 Server Side 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. + */ + +#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kNetwork + +#include "mongo/platform/basic.h" + +#include "mongo/base/init.h" +#include "mongo/config.h" +#include "mongo/util/log.h" +#include "mongo/util/net/ssl_manager.h" +#include "mongo/util/net/ssl_options.h" + +#include <boost/optional.hpp> +#include <openssl/err.h> +#include <openssl/ssl.h> +#include <stack> + +namespace mongo { +namespace { + +/** + * Multithreaded Support for SSL. + * + * In order to allow OpenSSL to work in a multithreaded environment, you + * may need to provide some callbacks for it to use for locking. The following code + * sets up a vector of mutexes and provides a thread unique ID number. + * The so-called SSLThreadInfo class encapsulates most of the logic required for + * OpenSSL multithreaded support. + * + * OpenSSL before version 1.1.0 requires applications provide a callback which emits a thread + * identifier. This ID is used to store thread specific ERR information. When a thread is + * terminated, it must call ERR_remove_state or ERR_remove_thread_state. These functions may + * themselves invoke the application provided callback. These IDs are stored in a hashtable with + * a questionable hash function. They must be uniformly distributed to prevent collisions. + */ +class SSLThreadInfo { +public: + static unsigned long getID() { + struct CallErrRemoveState { + explicit CallErrRemoveState(ThreadIDManager& manager, unsigned long id) + : _manager(manager), id(id) {} + + ~CallErrRemoveState() { + ERR_remove_state(0); + _manager.releaseID(id); + }; + + ThreadIDManager& _manager; + unsigned long id; + }; + + // NOTE: This logic is fully intentional. Because ERR_remove_state (called within + // the destructor of the kRemoveStateFromThread object) re-enters this function, + // we must have a two phase protection, otherwise we would access a thread local + // during its destruction. + static thread_local boost::optional<CallErrRemoveState> threadLocalState; + if (!threadLocalState) { + threadLocalState.emplace(_idManager, _idManager.reserveID()); + } + + return threadLocalState->id; + } + + static void lockingCallback(int mode, int type, const char* file, int line) { + if (mode & CRYPTO_LOCK) { + _mutex[type]->lock(); + } else { + _mutex[type]->unlock(); + } + } + + static void init() { + CRYPTO_set_id_callback(&SSLThreadInfo::getID); + CRYPTO_set_locking_callback(&SSLThreadInfo::lockingCallback); + + while ((int)_mutex.size() < CRYPTO_num_locks()) { + _mutex.emplace_back(std::make_unique<stdx::recursive_mutex>()); + } + } + +private: + SSLThreadInfo() = delete; + + // Note: see SERVER-8734 for why we are using a recursive mutex here. + // Once the deadlock fix in OpenSSL is incorporated into most distros of + // Linux, this can be changed back to a nonrecursive mutex. + static std::vector<std::unique_ptr<stdx::recursive_mutex>> _mutex; + + class ThreadIDManager { + public: + unsigned long reserveID() { + stdx::unique_lock<Latch> lock(_idMutex); + if (!_idLast.empty()) { + unsigned long ret = _idLast.top(); + _idLast.pop(); + return ret; + } + return ++_idNext; + } + + void releaseID(unsigned long id) { + stdx::unique_lock<Latch> lock(_idMutex); + _idLast.push(id); + } + + private: + // Machinery for producing IDs that are unique for the life of a thread. + Mutex _idMutex = + MONGO_MAKE_LATCH("ThreadIDManager::_idMutex"); // Protects _idNext and _idLast. + unsigned long _idNext = 0; // Stores the next thread ID to use, if none already allocated. + std::stack<unsigned long, std::vector<unsigned long>> + _idLast; // Stores old thread IDs, for reuse. + }; + static ThreadIDManager _idManager; +}; +std::vector<std::unique_ptr<stdx::recursive_mutex>> SSLThreadInfo::_mutex; +SSLThreadInfo::ThreadIDManager SSLThreadInfo::_idManager; + +void setupFIPS() { +// Turn on FIPS mode if requested, OPENSSL_FIPS must be defined by the OpenSSL headers +#if defined(MONGO_CONFIG_HAVE_FIPS_MODE_SET) + int status = FIPS_mode_set(1); + if (!status) { + severe() << "can't activate FIPS mode: " + << SSLManagerInterface::getSSLErrorMessage(ERR_get_error()); + fassertFailedNoTrace(16703); + } + log() << "FIPS 140-2 mode activated"; +#else + severe() << "this version of mongodb was not compiled with FIPS support"; + fassertFailedNoTrace(17089); +#endif +} + +MONGO_INITIALIZER(SetupOpenSSL)(InitializerContext*) { + SSL_library_init(); + SSL_load_error_strings(); + ERR_load_crypto_strings(); + + if (sslGlobalParams.sslFIPSMode) { + setupFIPS(); + } + + // Add all digests and ciphers to OpenSSL's internal table + // so that encryption/decryption is backwards compatible + OpenSSL_add_all_algorithms(); + + // Setup OpenSSL multithreading callbacks and mutexes + SSLThreadInfo::init(); + + return Status::OK(); +} + +} // namespace +} // namespace mongo diff --git a/src/mongo/util/net/ssl_manager_openssl.cpp b/src/mongo/util/net/ssl_manager_openssl.cpp index 91e7933f14e..16c8cdac376 100644 --- a/src/mongo/util/net/ssl_manager_openssl.cpp +++ b/src/mongo/util/net/ssl_manager_openssl.cpp @@ -275,104 +275,6 @@ void DH_get0_pqg(const DH* dh, const BIGNUM** p, const BIGNUM** q, const BIGNUM* } #endif -/** - * Multithreaded Support for SSL. - * - * In order to allow OpenSSL to work in a multithreaded environment, you - * may need to provide some callbacks for it to use for locking. The following code - * sets up a vector of mutexes and provides a thread unique ID number. - * The so-called SSLThreadInfo class encapsulates most of the logic required for - * OpenSSL multithreaded support. - * - * OpenSSL before version 1.1.0 requires applications provide a callback which emits a thread - * identifier. This ID is used to store thread specific ERR information. When a thread is - * terminated, it must call ERR_remove_state or ERR_remove_thread_state. These functions may - * themselves invoke the application provided callback. These IDs are stored in a hashtable with - * a questionable hash function. They must be uniformly distributed to prevent collisions. - */ -class SSLThreadInfo { -public: - static unsigned long getID() { - struct CallErrRemoveState { - explicit CallErrRemoveState(ThreadIDManager& manager, unsigned long id) - : _manager(manager), id(id) {} - - ~CallErrRemoveState() { - ERR_remove_state(0); - _manager.releaseID(id); - }; - - ThreadIDManager& _manager; - unsigned long id; - }; - - // NOTE: This logic is fully intentional. Because ERR_remove_state (called within - // the destructor of the kRemoveStateFromThread object) re-enters this function, - // we must have a two phase protection, otherwise we would access a thread local - // during its destruction. - static thread_local boost::optional<CallErrRemoveState> threadLocalState; - if (!threadLocalState) { - threadLocalState.emplace(_idManager, _idManager.reserveID()); - } - - return threadLocalState->id; - } - - static void lockingCallback(int mode, int type, const char* file, int line) { - if (mode & CRYPTO_LOCK) { - _mutex[type]->lock(); - } else { - _mutex[type]->unlock(); - } - } - - static void init() { - CRYPTO_set_id_callback(&SSLThreadInfo::getID); - CRYPTO_set_locking_callback(&SSLThreadInfo::lockingCallback); - - while ((int)_mutex.size() < CRYPTO_num_locks()) { - _mutex.emplace_back(std::make_unique<stdx::recursive_mutex>()); - } - } - -private: - SSLThreadInfo() = delete; - - // Note: see SERVER-8734 for why we are using a recursive mutex here. - // Once the deadlock fix in OpenSSL is incorporated into most distros of - // Linux, this can be changed back to a nonrecursive mutex. - static std::vector<std::unique_ptr<stdx::recursive_mutex>> _mutex; - - class ThreadIDManager { - public: - unsigned long reserveID() { - stdx::unique_lock<Latch> lock(_idMutex); - if (!_idLast.empty()) { - unsigned long ret = _idLast.top(); - _idLast.pop(); - return ret; - } - return ++_idNext; - } - - void releaseID(unsigned long id) { - stdx::unique_lock<Latch> lock(_idMutex); - _idLast.push(id); - } - - private: - // Machinery for producing IDs that are unique for the life of a thread. - Mutex _idMutex = - MONGO_MAKE_LATCH("ThreadIDManager::_idMutex"); // Protects _idNext and _idLast. - unsigned long _idNext = 0; // Stores the next thread ID to use, if none already allocated. - std::stack<unsigned long, std::vector<unsigned long>> - _idLast; // Stores old thread IDs, for reuse. - }; - static ThreadIDManager _idManager; -}; -std::vector<std::unique_ptr<stdx::recursive_mutex>> SSLThreadInfo::_mutex; -SSLThreadInfo::ThreadIDManager SSLThreadInfo::_idManager; - boost::optional<std::string> getRawSNIServerName(const SSL* const ssl) { const char* name = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); if (!name) { @@ -597,22 +499,6 @@ private: static int verify_cb(int ok, X509_STORE_CTX* ctx); }; -void setupFIPS() { -// Turn on FIPS mode if requested, OPENSSL_FIPS must be defined by the OpenSSL headers -#if defined(MONGO_CONFIG_HAVE_FIPS_MODE_SET) - int status = FIPS_mode_set(1); - if (!status) { - severe() << "can't activate FIPS mode: " - << SSLManagerInterface::getSSLErrorMessage(ERR_get_error()); - fassertFailedNoTrace(16703); - } - log() << "FIPS 140-2 mode activated"; -#else - severe() << "this version of mongodb was not compiled with FIPS support"; - fassertFailedNoTrace(17089); -#endif -} - } // namespace // Global variable indicating if this is a server or a client instance @@ -620,25 +506,6 @@ bool isSSLServer = false; extern SSLManagerInterface* theSSLManager; -MONGO_INITIALIZER(SetupOpenSSL)(InitializerContext*) { - SSL_library_init(); - SSL_load_error_strings(); - ERR_load_crypto_strings(); - - if (sslGlobalParams.sslFIPSMode) { - setupFIPS(); - } - - // Add all digests and ciphers to OpenSSL's internal table - // so that encryption/decryption is backwards compatible - OpenSSL_add_all_algorithms(); - - // Setup OpenSSL multithreading callbacks and mutexes - SSLThreadInfo::init(); - - return Status::OK(); -} - MONGO_INITIALIZER_WITH_PREREQUISITES(SSLManager, ("SetupOpenSSL", "EndStartupOptionHandling")) (InitializerContext*) { if (!isSSLServer || (sslGlobalParams.sslMode.load() != SSLParams::SSLMode_disabled)) { |