Commit 87659d07 authored by Anirudh Ramachandran's avatar Anirudh Ramachandran Committed by Facebook Github Bot

Add OpenSSL portability layer

Summary:
Enable folly to build with OpenSSL alternatives (e.g., BoringSSL).

Some SSL-related tests are disabled:

 - Async session cache ones (BoringSSL has an async cache impl but with a different API)
 - TFO tests
 - Some changes to error tests which expect specific error strings. This is
   flaky as a test because it will break everytime even within a version, so we
   should fix that first.

This should be a noop for OpenSSL 1.0.2.

BoringSSL commit used is 35c8afd3143289c99aa3820e01950c564d7aced8 (10/26/2016)

Closes: https://github.com/facebook/folly/issues/198

Reviewed By: siyengar

Differential Revision: D3280382

fbshipit-source-id: 4141d992e0d8dd797ac4af479cfe90844a23278f
parent ec5f59cf
......@@ -295,6 +295,7 @@ nobase_follyinclude_HEADERS = \
portability/Malloc.h \
portability/Math.h \
portability/Memory.h \
portability/OpenSSL.h \
portability/PThread.h \
portability/Sockets.h \
portability/Stdio.h \
......@@ -336,8 +337,8 @@ nobase_follyinclude_HEADERS = \
SpookyHashV1.h \
SpookyHashV2.h \
ssl/OpenSSLHash.h \
ssl/OpenSSLVersionFinder.h \
ssl/SSLSession.h \
ssl/detail/OpenSSLVersionFinder.h \
ssl/detail/SSLSessionImpl.h \
stats/BucketedTimeSeries-defs.h \
stats/BucketedTimeSeries.h \
......@@ -477,6 +478,7 @@ libfolly_la_SOURCES = \
portability/Libgen.cpp \
portability/Malloc.cpp \
portability/Memory.cpp \
portability/OpenSSL.cpp \
portability/Sockets.cpp \
portability/Stdio.cpp \
portability/Stdlib.cpp \
......
......@@ -31,8 +31,9 @@
#include <folly/Bits.h>
#include <folly/SocketAddress.h>
#include <folly/SpinLock.h>
#include <folly/io/IOBuf.h>
#include <folly/io/Cursor.h>
#include <folly/io/IOBuf.h>
#include <folly/portability/OpenSSL.h>
#include <folly/portability/Unistd.h>
using folly::SocketAddress;
......@@ -54,6 +55,8 @@ using folly::AsyncSocketException;
using folly::AsyncSSLSocket;
using folly::Optional;
using folly::SSLContext;
// For OpenSSL portability API
using namespace folly::ssl;
using folly::ssl::OpenSSLUtils;
// We have one single dummy SSL context so that we can implement attach
......@@ -475,8 +478,8 @@ void AsyncSSLSocket::attachSSLContext(
// previously called.
// We need to update the initial_ctx if necessary
auto sslCtx = ctx->getSSLCtx();
SSL_CTX_up_ref(sslCtx);
#ifndef OPENSSL_NO_TLSEXT
CRYPTO_add(&sslCtx->references, 1, CRYPTO_LOCK_SSL_CTX);
// note that detachSSLContext has already freed ssl_->initial_ctx
ssl_->initial_ctx = sslCtx;
#endif
......@@ -773,7 +776,8 @@ void AsyncSSLSocket::setSSLSession(SSL_SESSION *session, bool takeOwnership) {
sslSession_ = session;
if (!takeOwnership && session != nullptr) {
// Increment the reference count
CRYPTO_add(&session->references, 1, CRYPTO_LOCK_SSL_SESSION);
// This API exists in BoringSSL and OpenSSL 1.1.0
SSL_SESSION_up_ref(session);
}
}
......
......@@ -35,6 +35,9 @@ struct CRYPTO_dynlock_value {
};
namespace folly {
//
// For OpenSSL portability API
using namespace folly::ssl;
bool SSLContext::initialized_ = false;
......
......@@ -25,6 +25,7 @@
#include <random>
// This has to come before SSL.
#include <folly/portability/OpenSSL.h>
#include <folly/portability/Sockets.h>
#include <openssl/ssl.h>
......
......@@ -15,6 +15,7 @@
*/
#include <folly/io/async/ssl/OpenSSLUtils.h>
#include <folly/ScopeGuard.h>
#include <folly/portability/OpenSSL.h>
#include <folly/portability/Sockets.h>
#include <glog/logging.h>
#include <openssl/bio.h>
......@@ -24,12 +25,6 @@
#include <openssl/x509v3.h>
#include <unordered_map>
#define OPENSSL_IS_101 (OPENSSL_VERSION_NUMBER >= 0x1000105fL && \
OPENSSL_VERSION_NUMBER < 0x1000200fL)
#define OPENSSL_IS_102 (OPENSSL_VERSION_NUMBER >= 0x1000200fL && \
OPENSSL_VERSION_NUMBER < 0x10100000L)
#define OPENSSL_IS_110 (OPENSSL_VERSION_NUMBER >= 0x10100000L)
namespace {
#if defined(OPENSSL_IS_BORINGSSL)
// BoringSSL doesn't (as of May 2016) export the equivalent
......@@ -45,7 +40,7 @@ namespace ssl {
bool OpenSSLUtils::getTLSMasterKey(
const SSL_SESSION* session,
MutableByteRange keyOut) {
#if OPENSSL_IS_101 || OPENSSL_IS_102
#if FOLLY_OPENSSL_IS_101 || FOLLY_OPENSSL_IS_102
if (session &&
session->master_key_length == static_cast<int>(keyOut.size())) {
auto masterKey = session->master_key;
......@@ -53,6 +48,9 @@ bool OpenSSLUtils::getTLSMasterKey(
masterKey, masterKey + session->master_key_length, keyOut.begin());
return true;
}
#else
(SSL_SESSION*)session;
(MutableByteRange) keyOut;
#endif
return false;
}
......@@ -60,13 +58,16 @@ bool OpenSSLUtils::getTLSMasterKey(
bool OpenSSLUtils::getTLSClientRandom(
const SSL* ssl,
MutableByteRange randomOut) {
#if OPENSSL_IS_101 || OPENSSL_IS_102
#if FOLLY_OPENSSL_IS_101 || FOLLY_OPENSSL_IS_102
if ((SSL_version(ssl) >> 8) == TLS1_VERSION_MAJOR && ssl->s3 &&
randomOut.size() == SSL3_RANDOM_SIZE) {
auto clientRandom = ssl->s3->client_random;
std::copy(clientRandom, clientRandom + SSL3_RANDOM_SIZE, randomOut.begin());
return true;
}
#else
(SSL*)ssl;
(MutableByteRange) randomOut;
#endif
return false;
}
......@@ -121,7 +122,7 @@ bool OpenSSLUtils::validatePeerCertNames(X509* cert,
}
}
for (int i = 0; i < sk_GENERAL_NAME_num(altNames); i++) {
for (size_t i = 0; i < (size_t)sk_GENERAL_NAME_num(altNames); i++) {
auto name = sk_GENERAL_NAME_value(altNames, i);
if ((addr4 != nullptr || addr6 != nullptr) && name->type == GEN_IPADD) {
// Extra const-ness for paranoia
......@@ -199,13 +200,7 @@ bool OpenSSLUtils::setCustomBioReadMethod(
BIO_METHOD* bioMeth,
int (*meth)(BIO*, char*, int)) {
bool ret = false;
#if OPENSSL_IS_110
ret = (BIO_meth_set_read(bioMeth, meth) == 1);
#elif (defined(OPENSSL_IS_BORINGSSL) || OPENSSL_IS_101 || OPENSSL_IS_102)
bioMeth->bread = meth;
ret = true;
#endif
return ret;
}
......@@ -213,13 +208,7 @@ bool OpenSSLUtils::setCustomBioWriteMethod(
BIO_METHOD* bioMeth,
int (*meth)(BIO*, const char*, int)) {
bool ret = false;
#if OPENSSL_IS_110
ret = (BIO_meth_set_write(bioMeth, meth) == 1);
#elif (defined(OPENSSL_IS_BORINGSSL) || OPENSSL_IS_101 || OPENSSL_IS_102)
bioMeth->bwrite = meth;
ret = true;
#endif
return ret;
}
......
......@@ -26,15 +26,29 @@ using namespace folly;
TEST(SSLErrorsTest, TestMessage) {
ERR_load_crypto_strings();
auto err = ERR_PACK(
unsigned long err;
#ifdef OPENSSL_IS_BORINGSSL
err = ERR_PACK(ERR_LIB_X509, X509_R_CERT_ALREADY_IN_HASH_TABLE);
#else
err = ERR_PACK(
ERR_LIB_X509,
X509_F_X509_STORE_ADD_CERT,
X509_R_CERT_ALREADY_IN_HASH_TABLE);
#endif
SSLException ex(0, err, 0, 0);
// This is flaky - we should not be testing error strings
// which may change version to version
#if defined(OPENSSL_IS_BORINGSSL)
std::string expectedMsg =
"AsyncSocketException: error:0b000069:X.509 certificate routines:"
"OPENSSL_internal:CERT_ALREADY_IN_HASH_TABLE, type = SSL error";
#else
std::string expectedMsg =
"AsyncSocketException: error:0B07C065:"
"x509 certificate routines:X509_STORE_add_cert:"
"cert already in hash table, type = SSL error";
#endif
std::string actual = ex.what();
EXPECT_EQ(expectedMsg, actual);
}
......@@ -239,6 +239,7 @@ TEST(AsyncSSLSocketTest, ReadAfterClose) {
/**
* Test bad renegotiation
*/
#if !defined(OPENSSL_IS_BORINGSSL)
TEST(AsyncSSLSocketTest, Renegotiate) {
EventBase eventBase;
auto clientCtx = std::make_shared<SSLContext>();
......@@ -284,6 +285,7 @@ TEST(AsyncSSLSocketTest, Renegotiate) {
eventBase.loop();
ASSERT_TRUE(server.renegotiationError_);
}
#endif
/**
* Negative test for handshakeError().
......@@ -549,7 +551,18 @@ TEST_P(NextProtocolTest, NpnTestNoOverlap) {
// mismatch should result in a fatal alert, but this is OpenSSL's current
// behavior and we want to know if it changes.
expectNoProtocol();
} else {
}
#if defined(OPENSSL_IS_BORINGSSL)
// BoringSSL also doesn't fatal on mismatch but behaves slightly differently
// from OpenSSL 1.0.2h+ - it doesn't select a protocol if both ends support
// NPN *and* ALPN
else if (
GetParam().first == SSLContext::NextProtocolType::ANY &&
GetParam().second == SSLContext::NextProtocolType::ANY) {
expectNoProtocol();
}
#endif
else {
expectProtocol("blub");
expectProtocolType(
{SSLContext::NextProtocolType::NPN, SSLContext::NextProtocolType::NPN});
......@@ -877,7 +890,7 @@ TEST(AsyncSSLSocketTest, SSLClientTimeoutTest) {
cerr << "SSLClientTimeoutTest test completed" << endl;
}
// This is a FB-only extension, and the tests will fail without it
// The next 3 tests need an FB-only extension, and will fail without it
#ifdef SSL_ERROR_WANT_SESS_CACHE_LOOKUP
/**
* Test SSL server async cache
......@@ -907,7 +920,6 @@ TEST(AsyncSSLSocketTest, SSLServerAsyncCacheTest) {
cerr << "SSLServerAsyncCacheTest test completed" << endl;
}
/**
* Test SSL server accept timeout with cache path
*/
......@@ -999,7 +1011,7 @@ TEST(AsyncSSLSocketTest, SSLServerCacheCloseTest) {
cerr << "SSLServerCacheCloseTest test completed" << endl;
}
#endif
#endif // !SSL_ERROR_WANT_SESS_CACHE_LOOKUP
/**
* Verify Client Ciphers obtained using SSL MSG Callback.
......@@ -1016,7 +1028,7 @@ TEST(AsyncSSLSocketTest, SSLParseClientHelloSuccess) {
serverCtx->loadClientCAList(testCA);
clientCtx->setVerificationOption(SSLContext::SSLVerifyPeerEnum::VERIFY);
clientCtx->ciphers("AES256-SHA:RC4-MD5");
clientCtx->ciphers("AES256-SHA:AES128-SHA");
clientCtx->loadPrivateKey(testKey);
clientCtx->loadCertificate(testCert);
clientCtx->loadTrustedCertificates(testCA);
......@@ -1034,7 +1046,11 @@ TEST(AsyncSSLSocketTest, SSLParseClientHelloSuccess) {
eventBase.loop();
EXPECT_EQ(server.clientCiphers_, "AES256-SHA:RC4-MD5:00ff");
#if defined(OPENSSL_IS_BORINGSSL)
EXPECT_EQ(server.clientCiphers_, "AES256-SHA:AES128-SHA");
#else
EXPECT_EQ(server.clientCiphers_, "AES256-SHA:AES128-SHA:00ff");
#endif
EXPECT_EQ(server.chosenCipher_, "AES256-SHA");
EXPECT_TRUE(client.handshakeVerify_);
EXPECT_TRUE(client.handshakeSuccess_);
......@@ -1692,8 +1708,14 @@ TEST(AsyncSSLSocketTest, ConnOpenSSLErrorString) {
handshakeCallback.waitForHandshake();
EXPECT_NE(handshakeCallback.errorString_.find("SSL routines"),
std::string::npos);
#if defined(OPENSSL_IS_BORINGSSL)
EXPECT_NE(
handshakeCallback.errorString_.find("ENCRYPTED_LENGTH_TOO_LONG"),
std::string::npos);
#else
EXPECT_NE(handshakeCallback.errorString_.find("unknown protocol"),
std::string::npos);
#endif
}
TEST(AsyncSSLSocketTest, TestSSLCipherCodeToNameMap) {
......@@ -1868,6 +1890,7 @@ TEST(AsyncSSLSocketTest, ConnectWriteReadCloseTFOFallback) {
sock.close();
}
#if !defined(OPENSSL_IS_BORINGSSL)
TEST(AsyncSSLSocketTest, ConnectTFOTimeout) {
// Start listening on a local port
ConnectTimeoutCallback acceptCallback;
......@@ -1883,7 +1906,9 @@ TEST(AsyncSSLSocketTest, ConnectTFOTimeout) {
EXPECT_THROW(
socket->open(std::chrono::milliseconds(20)), AsyncSocketException);
}
#endif
#if !defined(OPENSSL_IS_BORINGSSL)
TEST(AsyncSSLSocketTest, ConnectTFOFallbackTimeout) {
// Start listening on a local port
ConnectTimeoutCallback acceptCallback;
......@@ -1899,6 +1924,7 @@ TEST(AsyncSSLSocketTest, ConnectTFOFallbackTimeout) {
evb.loop();
EXPECT_EQ(ConnCallback::State::ERROR, ccb.state);
}
#endif
TEST(AsyncSSLSocketTest, HandshakeTFOFallbackTimeout) {
// Start listening on a local port
......
......@@ -683,6 +683,7 @@ class TestSSLAsyncCacheServer : public TestSSLServer {
int* copyflag) {
*copyflag = 0;
asyncCallbacks_++;
(void)ssl;
#ifdef SSL_ERROR_WANT_SESS_CACHE_LOOKUP
if (!SSL_want_sess_cache_lookup(ssl)) {
// libssl.so mismatch
......
......@@ -197,9 +197,16 @@ int main(int argc, char *argv[]) {
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},
{CRYPTO_LOCK_SSL_CTX, folly::SSLContext::LOCK_NONE}});
#endif
#ifdef CRYPTO_LOCK_SSL_CTX
{CRYPTO_LOCK_SSL_CTX, folly::SSLContext::LOCK_NONE}
#endif
});
testing::InitGoogleTest(&argc, argv);
gflags::ParseCommandLineFlags(&argc, &argv, true);
return RUN_ALL_TESTS();
......
/*
* Copyright 2016 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/portability/OpenSSL.h>
namespace folly {
namespace ssl {
#ifdef OPENSSL_IS_BORINGSSL
int SSL_CTX_set1_sigalgs_list(SSL_CTX*, const char*) {
return 1; // 0 implies error
}
int TLS1_get_client_version(SSL* s) {
return s->client_version;
}
int BIO_meth_set_read(BIO_METHOD* biom, int (*read)(BIO*, char*, int)) {
biom->bread = read;
return 1;
}
int BIO_meth_set_write(BIO_METHOD* biom, int (*write)(BIO*, const char*, int)) {
biom->bwrite = write;
return 1;
}
#elif FOLLY_OPENSSL_IS_102 || FOLLY_OPENSSL_IS_101
int SSL_CTX_up_ref(SSL_CTX* ctx) {
return CRYPTO_add(&ctx->references, 1, CRYPTO_LOCK_SSL_CTX);
}
int SSL_SESSION_up_ref(SSL_SESSION* session) {
return CRYPTO_add(&session->references, 1, CRYPTO_LOCK_SSL_SESSION);
}
int X509_up_ref(X509* x) {
return CRYPTO_add(&x->references, 1, CRYPTO_LOCK_X509);
}
int BIO_meth_set_read(BIO_METHOD* biom, int (*read)(BIO*, char*, int)) {
biom->bread = read;
return 1;
}
int BIO_meth_set_write(BIO_METHOD* biom, int (*write)(BIO*, const char*, int)) {
biom->bwrite = write;
return 1;
}
#elif FOLLY_OPENSSL_IS_110
#endif
}
}
/*
* Copyright 2016 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 <openssl/ssl.h>
#include <openssl/x509.h>
namespace folly {
namespace ssl {
// BoringSSL doesn't have notion of versioning although it defines
// OPENSSL_VERSION_NUMBER to maintain compatibility. The following variables are
// intended to be specific to OpenSSL.
#if !defined(OPENSSL_IS_BORINGSSL)
#define FOLLY_OPENSSL_IS_101 \
(OPENSSL_VERSION_NUMBER >= 0x1000105fL && \
OPENSSL_VERSION_NUMBER < 0x1000200fL)
#define FOLLY_OPENSSL_IS_102 \
(OPENSSL_VERSION_NUMBER >= 0x1000200fL && \
OPENSSL_VERSION_NUMBER < 0x10100000L)
#define FOLLY_OPENSSL_IS_110 (OPENSSL_VERSION_NUMBER >= 0x10100000L)
#endif // !defined(OPENSSL_IS_BORINGSSL)
// This class attempts to "unify" the OpenSSL libssl APIs between OpenSSL 1.0.2,
// 1.1.0 and BoringSSL. The general idea is to provide wrapper methods for 1.0.2
// which already exist in BoringSSL and 1.1.0, but there are few APIs such as
// SSL_CTX_set1_sigalgs_list and so on which exist in 1.0.2 but were removed
// in BoringSSL
#ifdef OPENSSL_IS_BORINGSSL
int SSL_CTX_set1_sigalgs_list(SSL_CTX* ctx, const char* sigalgs_list);
int TLS1_get_client_version(SSL* s);
int BIO_meth_set_read(BIO_METHOD* biom, int (*read)(BIO*, char*, int));
int BIO_meth_set_write(BIO_METHOD* biom, int (*write)(BIO*, const char*, int));
#elif FOLLY_OPENSSL_IS_102 || FOLLY_OPENSSL_IS_101
int SSL_CTX_up_ref(SSL_CTX* session);
int SSL_SESSION_up_ref(SSL_SESSION* session);
int X509_up_ref(X509* x);
int BIO_meth_set_read(BIO_METHOD* biom, int (*read)(BIO*, char*, int));
int BIO_meth_set_write(BIO_METHOD* biom, int (*write)(BIO*, const char*, int));
#elif FOLLY_OPENSSL_IS_110
#else
#warning Compiling with unsupported OpenSSL version
#endif
} // ssl
} // folly
......@@ -16,23 +16,11 @@
#pragma once
#include <folly/Conv.h>
#include <folly/portability/OpenSSL.h>
#include <openssl/crypto.h>
#include <openssl/opensslv.h>
// BoringSSL doesn't have notion of versioning although it defines
// OPENSSL_VERSION_NUMBER to maintain compatibility. The following variables are
// intended to be specific to OpenSSL.
#if !defined(OPENSSL_IS_BORINGSSL)
# define OPENSSL_IS_101 \
(OPENSSL_VERSION_NUMBER >= 0x1000105fL && \
OPENSSL_VERSION_NUMBER < 0x1000200fL)
# define OPENSSL_IS_102 \
(OPENSSL_VERSION_NUMBER >= 0x1000200fL && \
OPENSSL_VERSION_NUMBER < 0x10100000L)
# define OPENSSL_IS_110 (OPENSSL_VERSION_NUMBER >= 0x10100000L)
#endif // !defined(OPENSSL_IS_BORINGSSL)
// This is used to find the OpenSSL version at runtime. Just returning
// OPENSSL_VERSION_NUMBER is insufficient as runtime version may be different
// from the compile-time version
......
......@@ -17,7 +17,7 @@
#pragma once
#include <folly/Memory.h>
#include <folly/ssl/detail/OpenSSLVersionFinder.h>
#include <folly/portability/OpenSSL.h>
#include <folly/ssl/detail/SSLSessionImpl.h>
namespace folly {
......
......@@ -15,7 +15,8 @@
*/
#include <folly/ssl/detail/SSLSessionImpl.h>
#include <folly/ssl/detail/OpenSSLVersionFinder.h>
#include <folly/portability/OpenSSL.h>
#include <folly/ssl/OpenSSLVersionFinder.h>
namespace folly {
namespace ssl {
......@@ -74,12 +75,7 @@ std::string SSLSessionImpl::getSessionID() const {
if (session_) {
const unsigned char* ptr = nullptr;
unsigned int len = 0;
#if defined(OPENSSL_IS_102) || defined(OPENSSL_IS_101)
len = session_->session_id_length;
ptr = session_->session_id;
#elif defined(OPENSSL_IS_110) || defined(OPENSSL_IS_BORINGSSL)
ptr = SSL_SESSION_get_id(session_, &len);
#endif
ret.assign(ptr, ptr + len);
}
return ret;
......@@ -96,11 +92,7 @@ SSL_SESSION* SSLSessionImpl::getRawSSLSessionDangerous() {
void SSLSessionImpl::upRef() {
if (session_) {
#if defined(OPENSSL_IS_102) || defined(OPENSSL_IS_101)
CRYPTO_add(&session_->references, 1, CRYPTO_LOCK_SSL_SESSION);
#elif defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_110)
SSL_SESSION_up_ref(session_);
#endif
}
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment