sorted_vector_types.h \
SparseByteSet.h \
SpinLock.h \
+ ssl/Init.h \
ssl/OpenSSLCertUtils.h \
ssl/OpenSSLHash.h \
ssl/OpenSSLPtrTypes.h \
ssl/OpenSSLVersionFinder.h \
ssl/SSLSession.h \
+ ssl/OpenSSLLockTypes.h \
+ ssl/detail/OpenSSLThreading.h \
ssl/detail/SSLSessionImpl.h \
stats/detail/Bucket.h \
stats/BucketedTimeSeries-defs.h \
Optional.cpp \
Singleton.cpp \
SocketAddress.cpp \
+ ssl/Init.cpp \
ssl/OpenSSLCertUtils.cpp \
ssl/OpenSSLHash.cpp \
+ ssl/detail/OpenSSLThreading.cpp \
ssl/detail/SSLSessionImpl.cpp \
stats/BucketedTimeSeries.cpp \
stats/Histogram.cpp \
#include <folly/SharedMutex.h>
#include <folly/SpinLock.h>
#include <folly/ThreadId.h>
+#include <folly/ssl/Init.h>
// ---------------------------------------------------------------------
// SSLContext implementation
// ---------------------------------------------------------------------
-
-struct CRYPTO_dynlock_value {
- std::mutex mutex;
-};
-
namespace folly {
//
// For OpenSSL portability API
using namespace folly::ssl;
-bool SSLContext::initialized_ = false;
-
-namespace {
-
-std::mutex& initMutex() {
- static std::mutex m;
- return m;
-}
-
-} // anonymous namespace
-
-#ifdef OPENSSL_NPN_NEGOTIATED
-int SSLContext::sNextProtocolsExDataIndex_ = -1;
-#endif
-
// SSLContext implementation
SSLContext::SSLContext(SSLVersion version) {
- {
- std::lock_guard<std::mutex> g(initMutex());
- initializeOpenSSLLocked();
- }
+ folly::ssl::init();
ctx_ = SSL_CTX_new(SSLv23_method());
if (ctx_ == nullptr) {
SSL_CTX_set_client_CA_list(ctx_, clientCAs);
}
-void SSLContext::randomize() {
- RAND_poll();
-}
-
void SSLContext::passwordCollector(
std::shared_ptr<PasswordCollector> collector) {
if (collector == nullptr) {
int SSLContext::advertisedNextProtocolCallback(SSL* ssl,
const unsigned char** out, unsigned int* outlen, void* data) {
+ static int nextProtocolsExDataIndex = SSL_get_ex_new_index(
+ 0, (void*)"Advertised next protocol index", nullptr, nullptr, nullptr);
+
SSLContext* context = (SSLContext*)data;
if (context == nullptr || context->advertisedNextProtocols_.empty()) {
*out = nullptr;
*out = context->advertisedNextProtocols_[0].protocols;
*outlen = context->advertisedNextProtocols_[0].length;
} else {
- uintptr_t selected_index = reinterpret_cast<uintptr_t>(SSL_get_ex_data(ssl,
- sNextProtocolsExDataIndex_));
+ uintptr_t selected_index = reinterpret_cast<uintptr_t>(
+ SSL_get_ex_data(ssl, nextProtocolsExDataIndex));
if (selected_index) {
--selected_index;
*out = context->advertisedNextProtocols_[selected_index].protocols;
} else {
auto i = context->pickNextProtocols();
uintptr_t selected = i + 1;
- SSL_set_ex_data(ssl, sNextProtocolsExDataIndex_, (void*)selected);
+ SSL_set_ex_data(ssl, nextProtocolsExDataIndex, (void*)selected);
*out = context->advertisedNextProtocols_[i].protocols;
*outlen = context->advertisedNextProtocols_[i].length;
}
return length;
}
-struct SSLLock {
- explicit SSLLock(
- SSLContext::SSLLockType inLockType = SSLContext::LOCK_MUTEX) :
- lockType(inLockType) {
- }
-
- void lock(bool read) {
- if (lockType == SSLContext::LOCK_MUTEX) {
- mutex.lock();
- } else if (lockType == SSLContext::LOCK_SPINLOCK) {
- spinLock.lock();
- } else if (lockType == SSLContext::LOCK_SHAREDMUTEX) {
- if (read) {
- sharedMutex.lock_shared();
- } else {
- sharedMutex.lock();
- }
- }
- // lockType == LOCK_NONE, no-op
- }
-
- void unlock(bool read) {
- if (lockType == SSLContext::LOCK_MUTEX) {
- mutex.unlock();
- } else if (lockType == SSLContext::LOCK_SPINLOCK) {
- spinLock.unlock();
- } else if (lockType == SSLContext::LOCK_SHAREDMUTEX) {
- if (read) {
- sharedMutex.unlock_shared();
- } else {
- sharedMutex.unlock();
- }
- }
- // lockType == LOCK_NONE, no-op
- }
-
- SSLContext::SSLLockType lockType;
- folly::SpinLock spinLock{};
- std::mutex mutex;
- SharedMutex sharedMutex;
-};
-
-// Statics are unsafe in environments that call exit().
-// If one thread calls exit() while another thread is
-// references a member of SSLContext, bad things can happen.
-// SSLContext runs in such environments.
-// Instead of declaring a static member we "new" the static
-// member so that it won't be destructed on exit().
-static std::unique_ptr<SSLLock[]>& locks() {
- static auto locksInst = new std::unique_ptr<SSLLock[]>();
- return *locksInst;
-}
-
-static std::map<int, SSLContext::SSLLockType>& lockTypes() {
- static auto lockTypesInst = new std::map<int, SSLContext::SSLLockType>();
- return *lockTypesInst;
-}
-
-static void callbackLocking(int mode, int n, const char*, int) {
- if (mode & CRYPTO_LOCK) {
- locks()[size_t(n)].lock(mode & CRYPTO_READ);
- } else {
- locks()[size_t(n)].unlock(mode & CRYPTO_READ);
- }
-}
-
-static unsigned long callbackThreadID() {
- return static_cast<unsigned long>(folly::getCurrentThreadID());
-}
-
-static CRYPTO_dynlock_value* dyn_create(const char*, int) {
- return new CRYPTO_dynlock_value;
-}
-
-static void dyn_lock(int mode,
- struct CRYPTO_dynlock_value* lock,
- const char*, int) {
- if (lock != nullptr) {
- if (mode & CRYPTO_LOCK) {
- lock->mutex.lock();
- } else {
- lock->mutex.unlock();
- }
- }
-}
-
-static void dyn_destroy(struct CRYPTO_dynlock_value* lock, const char*, int) {
- delete lock;
-}
-
-void SSLContext::setSSLLockTypesLocked(std::map<int, SSLLockType> inLockTypes) {
- lockTypes() = inLockTypes;
-}
-
-void SSLContext::setSSLLockTypes(std::map<int, SSLLockType> inLockTypes) {
- std::lock_guard<std::mutex> g(initMutex());
- if (initialized_) {
- // We set the locks on initialization, so if we are already initialized
- // this would have no affect.
- LOG(INFO) << "Ignoring setSSLLockTypes after initialization";
- return;
- }
- setSSLLockTypesLocked(std::move(inLockTypes));
-}
-
-void SSLContext::setSSLLockTypesAndInitOpenSSL(
- std::map<int, SSLLockType> inLockTypes) {
- std::lock_guard<std::mutex> g(initMutex());
- CHECK(!initialized_) << "OpenSSL is already initialized";
- setSSLLockTypesLocked(std::move(inLockTypes));
- initializeOpenSSLLocked();
-}
-
-bool SSLContext::isSSLLockDisabled(int lockId) {
- std::lock_guard<std::mutex> g(initMutex());
- CHECK(initialized_) << "OpenSSL is not initialized yet";
- const auto& sslLocks = lockTypes();
- const auto it = sslLocks.find(lockId);
- return it != sslLocks.end() &&
- it->second == SSLContext::SSLLockType::LOCK_NONE;
+void SSLContext::setSSLLockTypes(std::map<int, LockType> inLockTypes) {
+ folly::ssl::setLockTypes(inLockTypes);
}
#if defined(SSL_MODE_HANDSHAKE_CUTTHROUGH)
}
#endif
-void SSLContext::markInitialized() {
- std::lock_guard<std::mutex> g(initMutex());
- initialized_ = true;
-}
-
void SSLContext::initializeOpenSSL() {
- std::lock_guard<std::mutex> g(initMutex());
- initializeOpenSSLLocked();
-}
-
-void SSLContext::initializeOpenSSLLocked() {
- if (initialized_) {
- return;
- }
- SSL_library_init();
- SSL_load_error_strings();
- ERR_load_crypto_strings();
- // static locking
- locks().reset(new SSLLock[size_t(CRYPTO_num_locks())]);
- for (auto it: lockTypes()) {
- locks()[size_t(it.first)].lockType = it.second;
- }
- CRYPTO_set_id_callback(callbackThreadID);
- CRYPTO_set_locking_callback(callbackLocking);
- // dynamic locking
- CRYPTO_set_dynlock_create_callback(dyn_create);
- CRYPTO_set_dynlock_lock_callback(dyn_lock);
- CRYPTO_set_dynlock_destroy_callback(dyn_destroy);
- randomize();
-#ifdef OPENSSL_NPN_NEGOTIATED
- sNextProtocolsExDataIndex_ = SSL_get_ex_new_index(0,
- (void*)"Advertised next protocol index", nullptr, nullptr, nullptr);
-#endif
- initialized_ = true;
-}
-
-void SSLContext::cleanupOpenSSL() {
- std::lock_guard<std::mutex> g(initMutex());
- cleanupOpenSSLLocked();
-}
-
-void SSLContext::cleanupOpenSSLLocked() {
- if (!initialized_) {
- return;
- }
-
- CRYPTO_set_id_callback(nullptr);
- CRYPTO_set_locking_callback(nullptr);
- CRYPTO_set_dynlock_create_callback(nullptr);
- CRYPTO_set_dynlock_lock_callback(nullptr);
- CRYPTO_set_dynlock_destroy_callback(nullptr);
- CRYPTO_cleanup_all_ex_data();
- ERR_free_strings();
- EVP_cleanup();
- ERR_clear_error();
- locks().reset();
- initialized_ = false;
+ folly::ssl::init();
}
void SSLContext::setOptions(long options) {
#include <folly/folly-config.h>
#endif
+#include <folly/Portability.h>
#include <folly/Range.h>
#include <folly/io/async/ssl/OpenSSLUtils.h>
#include <folly/portability/OpenSSL.h>
+#include <folly/ssl/OpenSSLLockTypes.h>
#include <folly/ssl/OpenSSLPtrTypes.h>
namespace folly {
return ctx_;
}
- enum SSLLockType { LOCK_MUTEX, LOCK_SPINLOCK, LOCK_SHAREDMUTEX, LOCK_NONE };
-
/**
* Set preferences for how to treat locks in OpenSSL. This must be
* called before the instantiation of any SSLContext objects, otherwise
*
* setSSLLockTypes({{CRYPTO_LOCK_SSL_SESSION, SSLContext::LOCK_NONE}})
*/
- static void setSSLLockTypes(std::map<int, SSLLockType> lockTypes);
-
- /**
- * Set the lock types and initialize OpenSSL in an atomic fashion. This
- * aborts if the library has already been initialized.
- */
- static void setSSLLockTypesAndInitOpenSSL(
- std::map<int, SSLLockType> lockTypes);
-
- /**
- * Determine if the SSL lock with the specified id (i.e.
- * CRYPTO_LOCK_SSL_SESSION) is disabled. This should be called after
- * initializeOpenSSL. This will only check if the specified lock has been
- * explicitly set to LOCK_NONE.
- *
- * This is not safe to call while setSSLLockTypes is being called.
- */
- static bool isSSLLockDisabled(int lockId);
+ FOLLY_DEPRECATED("Use folly::ssl::setLockTypes")
+ static void setSSLLockTypes(std::map<int, ssl::LockType> lockTypes);
/**
* Examine OpenSSL's error stack, and return a string description of the
*/
static bool matchName(const char* host, const char* pattern, int size);
- /**
- * Functions for setting up and cleaning up openssl.
- * They can be invoked during the start of the application.
- */
+ FOLLY_DEPRECATED("Use folly::ssl::init")
static void initializeOpenSSL();
- static void cleanupOpenSSL();
-
- /**
- * Mark openssl as initialized without actually performing any initialization.
- * Please use this only if you are using a library which requires that it must
- * make its own calls to SSL_library_init() and related functions.
- */
- static void markInitialized();
-
- /**
- * Default randomize method.
- */
- static void randomize();
protected:
SSL_CTX* ctx_;
std::vector<int> advertisedNextProtocolWeights_;
std::discrete_distribution<int> nextProtocolDistribution_;
- static int sNextProtocolsExDataIndex_;
-
static int advertisedNextProtocolCallback(SSL* ssl,
const unsigned char** out, unsigned int* outlen, void* data);
static int selectNextProtocolCallback(
#endif
std::string providedCiphersString_;
-
- // Functions are called when locked by the calling function.
- static void initializeOpenSSLLocked();
- static void cleanupOpenSSLLocked();
- static void setSSLLockTypesLocked(std::map<int, SSLLockType> inLockTypes);
};
typedef std::shared_ptr<SSLContext> SSLContextPtr;
EXPECT_TRUE(f.within(std::chrono::seconds(3)).get());
}
-TEST(AsyncSSLSocketTest2, SSLContextLocks) {
- SSLContext::initializeOpenSSL();
-// these are checks based on the locks that are set in the main below
-#ifdef CRYPTO_LOCK_EVP_PKEY
- EXPECT_TRUE(SSLContext::isSSLLockDisabled(CRYPTO_LOCK_EVP_PKEY));
-#endif
-#ifdef CRYPTO_LOCK_SSL_SESSION
- EXPECT_FALSE(SSLContext::isSSLLockDisabled(CRYPTO_LOCK_SSL_SESSION));
-#endif
-#ifdef CRYPTO_LOCK_ERR
- EXPECT_FALSE(SSLContext::isSSLLockDisabled(CRYPTO_LOCK_ERR));
-#endif
-}
-TEST(AsyncSSLSocketTest2, SSLContextLocksSetAfterInitIgnored) {
- SSLContext::initializeOpenSSL();
- SSLContext::setSSLLockTypes({});
-#ifdef CRYPTO_LOCK_EVP_PKEY
- EXPECT_TRUE(SSLContext::isSSLLockDisabled(CRYPTO_LOCK_EVP_PKEY));
-#endif
-}
-
-TEST(AsyncSSLSocketTest2, SSLContextSetLocksAndInitialize) {
- SSLContext::cleanupOpenSSL();
- SSLContext::setSSLLockTypesAndInitOpenSSL({});
- EXPECT_DEATH(
- SSLContext::setSSLLockTypesAndInitOpenSSL({}),
- "OpenSSL is already initialized");
-
- SSLContext::cleanupOpenSSL();
- SSLContext::initializeOpenSSL();
- EXPECT_DEATH(
- SSLContext::setSSLLockTypesAndInitOpenSSL({}),
- "OpenSSL is already initialized");
-}
} // folly
int main(int argc, char *argv[]) {
#ifdef SIGPIPE
signal(SIGPIPE, SIG_IGN);
#endif
- folly::SSLContext::setSSLLockTypes({
-#ifdef CRYPTO_LOCK_EVP_PKEY
- {CRYPTO_LOCK_EVP_PKEY, folly::SSLContext::LOCK_NONE},
-#endif
-#ifdef CRYPTO_LOCK_SSL_SESSION
- {CRYPTO_LOCK_SSL_SESSION, folly::SSLContext::LOCK_SPINLOCK},
-#endif
-#ifdef CRYPTO_LOCK_SSL_CTX
- {CRYPTO_LOCK_SSL_CTX, folly::SSLContext::LOCK_NONE}
-#endif
- });
testing::InitGoogleTest(&argc, argv);
folly::init(&argc, &argv);
return RUN_ALL_TESTS();
#include <folly/io/async/AsyncSocketException.h>
#include <folly/io/async/SSLContext.h>
#include <folly/io/async/ssl/SSLErrors.h>
+#include <folly/ssl/Init.h>
#include <folly/portability/GTest.h>
#include <folly/portability/OpenSSL.h>
TEST(AsyncSocketException, SSLExceptionType) {
{
// Initiailzes OpenSSL everything. Else some of the calls will block
- folly::SSLContext::initializeOpenSSL();
+ folly::ssl::init();
SSLException eof(SSL_ERROR_ZERO_RETURN, 0, 0, 0);
EXPECT_EQ(eof.getType(), AsyncSocketException::END_OF_FILE);
--- /dev/null
+/*
+ * Copyright 2017-present Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <folly/io/async/test/AsyncSSLSocketTest.h>
+#include <functional>
+
+#include <folly/init/Init.h>
+#include <folly/io/async/SSLContext.h>
+#include <folly/portability/GTest.h>
+#include <folly/ssl/Init.h>
+
+namespace folly {
+
+void setupSSLLocks() {
+ folly::ssl::setLockTypes({
+#ifdef CRYPTO_LOCK_EVP_PKEY
+ {CRYPTO_LOCK_EVP_PKEY, folly::ssl::LockType::NONE},
+#endif
+#ifdef CRYPTO_LOCK_SSL_SESSION
+ {CRYPTO_LOCK_SSL_SESSION, folly::ssl::LockType::SPINLOCK},
+#endif
+#ifdef CRYPTO_LOCK_SSL_CTX
+ {CRYPTO_LOCK_SSL_CTX, folly::ssl::LockType::NONE}
+#endif
+ });
+}
+
+TEST(SSLContextInitializationTest, SSLContextInitializeThenSetLocksAndInit) {
+ EXPECT_DEATH(
+ {
+ folly::ssl::init();
+ folly::ssl::setLockTypesAndInit({});
+ },
+ "OpenSSL is already initialized");
+}
+
+TEST(SSLContextInitializationTest, SSLContextSetLocksAndInitialize) {
+ EXPECT_DEATH(
+ {
+ folly::ssl::setLockTypesAndInit({});
+ folly::ssl::setLockTypesAndInit({});
+ },
+ "OpenSSL is already initialized");
+}
+
+TEST(SSLContextInitializationTest, SSLContextLocks) {
+ EXPECT_EXIT(
+ {
+ setupSSLLocks();
+ folly::ssl::init();
+#ifdef CRYPTO_LOCK_EVP_PKEY
+ EXPECT_TRUE(folly::ssl::isLockDisabled(CRYPTO_LOCK_EVP_PKEY));
+#endif
+#ifdef CRYPTO_LOCK_SSL_SESSION
+ EXPECT_FALSE(folly::ssl::isLockDisabled(CRYPTO_LOCK_SSL_SESSION));
+#endif
+#ifdef CRYPTO_LOCK_ERR
+ EXPECT_FALSE(folly::ssl::isLockDisabled(CRYPTO_LOCK_ERR));
+#endif
+ if (::testing::Test::HasFailure()) {
+ exit(1);
+ }
+ LOG(INFO) << "SSLContextLocks passed";
+ exit(0);
+ },
+ ::testing::ExitedWithCode(0),
+ "SSLContextLocks passed");
+}
+
+TEST(SSLContextInitializationTest, SSLContextLocksSetAfterInitIgnored) {
+ EXPECT_EXIT(
+ {
+ setupSSLLocks();
+ folly::ssl::init();
+ folly::ssl::setLockTypes({});
+#ifdef CRYPTO_LOCK_EVP_PKEY
+ EXPECT_TRUE(folly::ssl::isLockDisabled(CRYPTO_LOCK_EVP_PKEY));
+#endif
+ if (::testing::Test::HasFailure()) {
+ exit(1);
+ }
+ LOG(INFO) << "SSLContextLocksSetAfterInitIgnored passed";
+ exit(0);
+ },
+ ::testing::ExitedWithCode(0),
+ "SSLContextLocksSetAfterInitIgnored passed");
+}
+} // folly
+
+int main(int argc, char* argv[]) {
+#ifdef SIGPIPE
+ signal(SIGPIPE, SIG_IGN);
+#endif
+ testing::InitGoogleTest(&argc, argv);
+ folly::init(&argc, &argv);
+
+ return RUN_ALL_TESTS();
+}
* limitations under the License.
*/
#include <folly/portability/OpenSSL.h>
+#include <folly/ssl/detail/OpenSSLThreading.h>
#include <stdexcept>
*ps = sig->s;
}
}
-#endif
+
+/**
+ * Compatibility shim for OpenSSL < 1.1.0.
+ *
+ * For now, options and settings are ignored. We implement the most common
+ * behavior, which is to add all digests, ciphers, and strings.
+ */
+int OPENSSL_init_ssl(uint64_t, const OPENSSL_INIT_SETTINGS*) {
+ // OpenSSL >= 1.1.0 handles initializing the library, adding digests &
+ // ciphers, loading strings. Additionally, OpenSSL >= 1.1.0 uses platform
+ // native threading & mutexes, which means that we should handle setting up
+ // the necessary threading initialization in the compat layer as well.
+ SSL_library_init();
+ OpenSSL_add_all_ciphers();
+ OpenSSL_add_all_digests();
+ OpenSSL_add_all_algorithms();
+
+ SSL_load_error_strings();
+ ERR_load_crypto_strings();
+
+ // The caller should have used SSLContext::setLockTypes() prior to calling
+ // this function.
+ folly::ssl::detail::installThreadingLocks();
+ return 0;
+}
+
+void OPENSSL_cleanup() {
+ folly::ssl::detail::cleanupThreadingLocks();
+ CRYPTO_cleanup_all_ex_data();
+ ERR_free_strings();
+ EVP_cleanup();
+ ERR_clear_error();
+}
+
+#endif // !FOLLY_OPENSSL_IS_110
}
}
}
#pragma once
+#include <cstdint>
+
// This must come before the OpenSSL includes.
#include <folly/portability/Windows.h>
const BIGNUM** iqmp);
int ECDSA_SIG_set0(ECDSA_SIG* sig, BIGNUM* r, BIGNUM* s);
void ECDSA_SIG_get0(const ECDSA_SIG* sig, const BIGNUM** pr, const BIGNUM** ps);
+
+using OPENSSL_INIT_SETTINGS = void;
+int OPENSSL_init_ssl(uint64_t opts, const OPENSSL_INIT_SETTINGS* settings);
+void OPENSSL_cleanup();
+
#endif
#if FOLLY_OPENSSL_IS_110
--- /dev/null
+/*
+ * Copyright 2017-present Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <folly/ssl/Init.h>
+
+#include <mutex>
+
+#include <folly/portability/OpenSSL.h>
+#include <folly/ssl/detail/OpenSSLThreading.h>
+#include <glog/logging.h>
+
+namespace folly {
+namespace ssl {
+
+namespace {
+bool initialized_ = false;
+
+std::mutex& initMutex() {
+ static std::mutex m;
+ return m;
+}
+
+void initializeOpenSSLLocked() {
+ if (initialized_) {
+ return;
+ }
+ OPENSSL_init_ssl(0, nullptr);
+ randomize();
+ initialized_ = true;
+}
+
+void cleanupOpenSSLLocked() {
+ if (!initialized_) {
+ return;
+ }
+
+ OPENSSL_cleanup();
+ initialized_ = false;
+}
+}
+
+void init() {
+ std::lock_guard<std::mutex> g(initMutex());
+ initializeOpenSSLLocked();
+}
+
+void cleanup() {
+ std::lock_guard<std::mutex> g(initMutex());
+ cleanupOpenSSLLocked();
+}
+
+void markInitialized() {
+ std::lock_guard<std::mutex> g(initMutex());
+ initialized_ = true;
+}
+
+void setLockTypesAndInit(LockTypeMapping inLockTypes) {
+ std::lock_guard<std::mutex> g(initMutex());
+ CHECK(!initialized_) << "OpenSSL is already initialized";
+ detail::setLockTypes(std::move(inLockTypes));
+ initializeOpenSSLLocked();
+}
+
+void setLockTypes(LockTypeMapping inLockTypes) {
+ std::lock_guard<std::mutex> g(initMutex());
+ if (initialized_) {
+ // We set the locks on initialization, so if we are already initialized
+ // this would have no affect.
+ LOG(INFO) << "Ignoring setSSLLockTypes after initialization";
+ return;
+ }
+ detail::setLockTypes(std::move(inLockTypes));
+}
+
+void randomize() {
+ RAND_poll();
+}
+
+bool isLockDisabled(int lockId) {
+ return detail::isSSLLockDisabled(lockId);
+}
+
+} // ssl
+} // folly
--- /dev/null
+/*
+ * Copyright 2004-present Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <map>
+
+#include <folly/ssl/OpenSSLLockTypes.h>
+
+namespace folly {
+namespace ssl {
+/**
+ * Initializes openssl. This should be invoked once, during the start of an
+ * application. For OpenSSL < 1.1.0, any lock types should be set with
+ * setLockTypes prior to the call to folly::ssl::init()
+ */
+void init();
+
+/**
+ * Cleans up openssl. This should be invoked at most once during the lifetime
+ * of the application. OpenSSL >= 1.1.0 users do not need to manually invoke
+ * this method, as OpenSSL will automatically cleanup itself during the exit
+ * of the application.
+ */
+void cleanup();
+
+/**
+ * Mark openssl as initialized without actually performing any initialization.
+ * Please use this only if you are using a library which requires that it must
+ * make its own calls to SSL_library_init() and related functions.
+ */
+void markInitialized();
+
+/**
+ * Set preferences for how to treat locks in OpenSSL. This must be
+ * called before folly::ssl::init(), otherwise the defaults will be used.
+ *
+ * OpenSSL has a lock for each module rather than for each object or
+ * data that needs locking. Some locks protect only refcounts, and
+ * might be better as spinlocks rather than mutexes. Other locks
+ * may be totally unnecessary if the objects being protected are not
+ * shared between threads in the application.
+ *
+ * For a list of OpenSSL lock types, refer to crypto/crypto.h.
+ *
+ * By default, all locks are initialized as mutexes. OpenSSL's lock usage
+ * may change from version to version and you should know what you are doing
+ * before disabling any locks entirely.
+ *
+ * In newer versions of OpenSSL (>= 1.1.0), OpenSSL manages its own locks,
+ * and this function is a no-op.
+ *
+ * Example: if you don't share SSL sessions between threads in your
+ * application, you may be able to do this
+ *
+ * setSSLLockTypes({{
+ * CRYPTO_LOCK_SSL_SESSION,
+ * SSLContext::SSLLockType::LOCK_NONE
+ * }})
+ */
+void setLockTypes(LockTypeMapping inLockTypes);
+
+/**
+ * Set the lock types and initialize OpenSSL in an atomic fashion. This
+ * aborts if the library has already been initialized.
+ */
+void setLockTypesAndInit(LockTypeMapping lockTypes);
+
+bool isLockDisabled(int lockId);
+
+void randomize();
+
+} // ssl
+} // folly
--- /dev/null
+/*
+ * Copyright 2004-present Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <map>
+
+namespace folly {
+namespace ssl {
+
+enum class LockType { MUTEX, SPINLOCK, SHAREDMUTEX, NONE };
+
+/**
+ * Map between an OpenSSL lock (see constants in crypto/crypto.h) and the
+ * implementation of the lock
+ */
+using LockTypeMapping = std::map<int, LockType>;
+
+} // ssl
+} // folly
--- /dev/null
+/*
+ * Copyright 2004-present Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <folly/ssl/detail/OpenSSLThreading.h>
+
+#include <mutex>
+
+#include <folly/Portability.h>
+#include <folly/SharedMutex.h>
+#include <folly/SpinLock.h>
+
+#include <glog/logging.h>
+
+// We cannot directly use portability/openssl because it also depends on us.
+// Therefore we directly use openssl includes. Order of includes is important
+// here. See portability/openssl.h.
+#include <folly/portability/Windows.h>
+#include <openssl/crypto.h>
+
+#if !defined(OPENSSL_IS_BORINGSSL)
+#define FOLLY_SSL_DETAIL_OPENSSL_IS_110 (OPENSSL_VERSION_NUMBER >= 0x10100000L)
+#else
+#define FOLLY_SSL_DETAIL_OPENSSL_IS_110 (false)
+#endif
+
+// OpenSSL requires us to provide the implementation of CRYPTO_dynlock_value
+// so it must be done in the global namespace.
+struct CRYPTO_dynlock_value {
+ std::mutex mutex;
+};
+
+namespace folly {
+namespace ssl {
+namespace detail {
+
+static std::map<int, LockType>& lockTypes() {
+ static auto lockTypesInst = new std::map<int, LockType>();
+ return *lockTypesInst;
+}
+
+void setLockTypes(std::map<int, LockType> inLockTypes) {
+#if FOLLY_SSL_DETAIL_OPENSSL_IS_110
+ LOG(INFO) << "setLockTypes() is unsupported on OpenSSL >= 1.1.0. "
+ << "OpenSSL now uses platform native mutexes";
+#endif
+
+ lockTypes() = inLockTypes;
+}
+
+bool isSSLLockDisabled(int lockId) {
+ const auto& sslLocks = lockTypes();
+ const auto it = sslLocks.find(lockId);
+ return it != sslLocks.end() && it->second == LockType::NONE;
+}
+
+namespace {
+struct SSLLock {
+ explicit SSLLock(LockType inLockType = LockType::MUTEX)
+ : lockType(inLockType) {}
+
+ void lock(bool read) {
+ if (lockType == LockType::MUTEX) {
+ mutex.lock();
+ } else if (lockType == LockType::SPINLOCK) {
+ spinLock.lock();
+ } else if (lockType == LockType::SHAREDMUTEX) {
+ if (read) {
+ sharedMutex.lock_shared();
+ } else {
+ sharedMutex.lock();
+ }
+ }
+ // lockType == LOCK_NONE, no-op
+ }
+
+ void unlock(bool read) {
+ if (lockType == LockType::MUTEX) {
+ mutex.unlock();
+ } else if (lockType == LockType::SPINLOCK) {
+ spinLock.unlock();
+ } else if (lockType == LockType::SHAREDMUTEX) {
+ if (read) {
+ sharedMutex.unlock_shared();
+ } else {
+ sharedMutex.unlock();
+ }
+ }
+ // lockType == LOCK_NONE, no-op
+ }
+
+ LockType lockType;
+ folly::SpinLock spinLock{};
+ std::mutex mutex;
+ SharedMutex sharedMutex;
+};
+} // end anonymous namespace
+
+// Statics are unsafe in environments that call exit().
+// If one thread calls exit() while another thread is
+// references a member of SSLContext, bad things can happen.
+// SSLContext runs in such environments.
+// Instead of declaring a static member we "new" the static
+// member so that it won't be destructed on exit().
+static std::unique_ptr<SSLLock[]>& locks() {
+ static auto locksInst = new std::unique_ptr<SSLLock[]>();
+ return *locksInst;
+}
+
+static void callbackLocking(int mode, int n, const char*, int) {
+ if (mode & CRYPTO_LOCK) {
+ locks()[size_t(n)].lock(mode & CRYPTO_READ);
+ } else {
+ locks()[size_t(n)].unlock(mode & CRYPTO_READ);
+ }
+}
+
+static unsigned long callbackThreadID() {
+ return static_cast<unsigned long>(folly::getCurrentThreadID());
+}
+
+static CRYPTO_dynlock_value* dyn_create(const char*, int) {
+ return new CRYPTO_dynlock_value;
+}
+
+static void
+dyn_lock(int mode, struct CRYPTO_dynlock_value* lock, const char*, int) {
+ if (lock != nullptr) {
+ if (mode & CRYPTO_LOCK) {
+ lock->mutex.lock();
+ } else {
+ lock->mutex.unlock();
+ }
+ }
+}
+
+static void dyn_destroy(struct CRYPTO_dynlock_value* lock, const char*, int) {
+ delete lock;
+}
+
+void installThreadingLocks() {
+ // static locking
+ locks().reset(new SSLLock[size_t(CRYPTO_num_locks())]);
+ for (auto it : lockTypes()) {
+ locks()[size_t(it.first)].lockType = it.second;
+ }
+ CRYPTO_set_id_callback(callbackThreadID);
+ CRYPTO_set_locking_callback(callbackLocking);
+ // dynamic locking
+ CRYPTO_set_dynlock_create_callback(dyn_create);
+ CRYPTO_set_dynlock_lock_callback(dyn_lock);
+ CRYPTO_set_dynlock_destroy_callback(dyn_destroy);
+}
+
+void cleanupThreadingLocks() {
+ CRYPTO_set_id_callback(nullptr);
+ CRYPTO_set_locking_callback(nullptr);
+ CRYPTO_set_dynlock_create_callback(nullptr);
+ CRYPTO_set_dynlock_lock_callback(nullptr);
+ CRYPTO_set_dynlock_destroy_callback(nullptr);
+ locks().reset();
+}
+
+} // detail
+} // ssl
+} // folly
--- /dev/null
+/*
+ * Copyright 2004-present Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <map>
+
+#include <folly/ssl/OpenSSLLockTypes.h>
+
+namespace folly {
+namespace ssl {
+namespace detail {
+bool isSSLLockDisabled(int lockId);
+void setLockTypes(std::map<int, LockType> inLockTypes);
+void installThreadingLocks();
+void cleanupThreadingLocks();
+}
+}
+}
#include <folly/Range.h>
#include <folly/String.h>
-#include <folly/io/async/SSLContext.h>
#include <folly/portability/GTest.h>
#include <folly/portability/OpenSSL.h>
#include <folly/ssl/OpenSSLPtrTypes.h>
+#include <folly/ssl/Init.H>
using namespace testing;
using namespace folly;
class OpenSSLCertUtilsTest : public Test {
public:
void SetUp() override {
- SSLContext::initializeOpenSSL();
+ folly::ssl::init();
}
};