/*
- * Copyright 2016 Facebook, Inc.
+ * Copyright 2017 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include "SSLContext.h"
-#include <openssl/err.h>
-#include <openssl/rand.h>
-#include <openssl/ssl.h>
-#include <openssl/x509v3.h>
-
#include <folly/Format.h>
#include <folly/Memory.h>
#include <folly/Random.h>
+#include <folly/SharedMutex.h>
#include <folly/SpinLock.h>
+#include <folly/ThreadId.h>
// ---------------------------------------------------------------------
// SSLContext implementation
};
namespace folly {
+//
+// For OpenSSL portability API
+using namespace folly::ssl;
bool SSLContext::initialized_ = false;
SSL_CTX_set_options(ctx_, SSL_OP_NO_COMPRESSION);
-#if OPENSSL_VERSION_NUMBER >= 0x1000105fL && !defined(OPENSSL_NO_TLSEXT)
+#if FOLLY_OPENSSL_HAS_SNI
SSL_CTX_set_tlsext_servername_callback(ctx_, baseServerNameOpenSSLCallback);
SSL_CTX_set_tlsext_servername_arg(ctx_, this);
#endif
}
void SSLContext::ciphers(const std::string& ciphers) {
- providedCiphersString_ = ciphers;
setCiphersOrThrow(ciphers);
}
}
void SSLContext::setServerECCurve(const std::string& curveName) {
- bool validCall = false;
-#if OPENSSL_VERSION_NUMBER >= 0x0090800fL
-#ifndef OPENSSL_NO_ECDH
- validCall = true;
-#endif
-#endif
- if (!validCall) {
- throw std::runtime_error("Elliptic curve encryption not allowed");
- }
-
+#if OPENSSL_VERSION_NUMBER >= 0x0090800fL && !defined(OPENSSL_NO_ECDH)
EC_KEY* ecdh = nullptr;
int nid;
nid = OBJ_sn2nid(curveName.c_str());
if (nid == 0) {
LOG(FATAL) << "Unknown curve name:" << curveName.c_str();
- return;
}
ecdh = EC_KEY_new_by_curve_name(nid);
if (ecdh == nullptr) {
LOG(FATAL) << "Unable to create curve:" << curveName.c_str();
- return;
}
SSL_CTX_set_tmp_ecdh(ctx_, ecdh);
EC_KEY_free(ecdh);
+#else
+ throw std::runtime_error("Elliptic curve encryption not allowed");
+#endif
}
void SSLContext::setX509VerifyParam(
if (rc == 0) {
throw std::runtime_error("SSL_CTX_set_cipher_list: " + getErrors());
}
+ providedCiphersString_ = ciphers;
}
void SSLContext::setVerificationOption(const SSLContext::SSLVerifyPeerEnum&
if (SSL_CTX_load_verify_locations(ctx_, path, nullptr) == 0) {
throw std::runtime_error("SSL_CTX_load_verify_locations: " + getErrors());
}
+ ERR_clear_error();
}
void SSLContext::loadTrustedCertificates(X509_STORE* store) {
SSL_CTX_set_default_passwd_cb_userdata(ctx_, this);
}
-#if OPENSSL_VERSION_NUMBER >= 0x1000105fL && !defined(OPENSSL_NO_TLSEXT)
+#if FOLLY_OPENSSL_HAS_SNI
void SSLContext::setServerNameCallback(const ServerNameCallback& cb) {
serverNameCb_ = cb;
<< ", but tls11AltCipherlist is of length "
<< tls11AltCipherlist.size();
} else {
- ciphers = &tls11AltCipherlist[index].first;
+ ciphers = &tls11AltCipherlist[size_t(index)].first;
}
}
SSL_set_cipher_list(ssl, providedCiphersString_.c_str());
}
}
-#endif
+#endif // FOLLY_OPENSSL_HAS_SNI
-#if OPENSSL_VERSION_NUMBER >= 0x1000200fL && !defined(OPENSSL_NO_TLSEXT)
+#if FOLLY_OPENSSL_HAS_ALPN
int SSLContext::alpnSelectCallback(SSL* /* ssl */,
const unsigned char** out,
unsigned char* outlen,
}
return SSL_TLSEXT_ERR_OK;
}
-#endif
+#endif // FOLLY_OPENSSL_HAS_ALPN
#ifdef OPENSSL_NPN_NEGOTIATED
ctx_, advertisedNextProtocolCallback, this);
SSL_CTX_set_next_proto_select_cb(ctx_, selectNextProtocolCallback, this);
}
-#if OPENSSL_VERSION_NUMBER >= 0x1000200fL && !defined(OPENSSL_NO_TLSEXT)
+#if FOLLY_OPENSSL_HAS_ALPN
if ((uint8_t)protocolType & (uint8_t)NextProtocolType::ALPN) {
SSL_CTX_set_alpn_select_cb(ctx_, alpnSelectCallback, this);
// Client cannot really use randomized alpn
deleteNextProtocolsStrings();
SSL_CTX_set_next_protos_advertised_cb(ctx_, nullptr, nullptr);
SSL_CTX_set_next_proto_select_cb(ctx_, nullptr, nullptr);
-#if OPENSSL_VERSION_NUMBER >= 0x1000200fL && !defined(OPENSSL_NO_TLSEXT)
+#if FOLLY_OPENSSL_HAS_ALPN
SSL_CTX_set_alpn_select_cb(ctx_, nullptr, nullptr);
SSL_CTX_set_alpn_protos(ctx_, nullptr, 0);
#endif
size_t SSLContext::pickNextProtocols() {
CHECK(!advertisedNextProtocols_.empty()) << "Failed to pickNextProtocols";
auto rng = ThreadLocalPRNG();
- return nextProtocolDistribution_(rng);
+ return size_t(nextProtocolDistribution_(rng));
}
int SSLContext::advertisedNextProtocolCallback(SSL* ssl,
SSL_CTX_set_session_id_context(
ctx_,
reinterpret_cast<const unsigned char*>(context.data()),
- std::min(
- static_cast<int>(context.length()), SSL_MAX_SSL_SESSION_ID_LENGTH));
+ std::min<unsigned int>(
+ static_cast<unsigned int>(context.length()),
+ SSL_MAX_SSL_SESSION_ID_LENGTH));
}
/**
if (length > size) {
length = size;
}
- strncpy(password, userPassword.c_str(), length);
+ strncpy(password, userPassword.c_str(), size_t(length));
return length;
}
lockType(inLockType) {
}
- void lock() {
+ 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() {
+ 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().
static void callbackLocking(int mode, int n, const char*, int) {
if (mode & CRYPTO_LOCK) {
- locks()[n].lock();
+ locks()[size_t(n)].lock(mode & CRYPTO_READ);
} else {
- locks()[n].unlock();
+ locks()[size_t(n)].unlock(mode & CRYPTO_READ);
}
}
static unsigned long callbackThreadID() {
- return static_cast<unsigned long>(
-#ifdef __APPLE__
- pthread_mach_thread_np(pthread_self())
-#elif _MSC_VER
- pthread_getw32threadid_np(pthread_self())
-#else
- pthread_self()
-#endif
- );
+ return static_cast<unsigned long>(folly::getCurrentThreadID());
}
static CRYPTO_dynlock_value* dyn_create(const char*, int) {
delete lock;
}
-void SSLContext::setSSLLockTypes(std::map<int, SSLLockType> inLockTypes) {
+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;
+}
+
#if defined(SSL_MODE_HANDSHAKE_CUTTHROUGH)
void SSLContext::enableFalseStart() {
SSL_CTX_set_mode(ctx_, SSL_MODE_HANDSHAKE_CUTTHROUGH);
SSL_load_error_strings();
ERR_load_crypto_strings();
// static locking
- locks().reset(new SSLLock[::CRYPTO_num_locks()]);
+ locks().reset(new SSLLock[size_t(CRYPTO_num_locks())]);
for (auto it: lockTypes()) {
- locks()[it.first].lockType = it.second;
+ locks()[size_t(it.first)].lockType = it.second;
}
CRYPTO_set_id_callback(callbackThreadID);
CRYPTO_set_locking_callback(callbackLocking);
CRYPTO_cleanup_all_ex_data();
ERR_free_strings();
EVP_cleanup();
- ERR_remove_state(0);
+ ERR_clear_error();
locks().reset();
initialized_ = false;
}