SpookyHashV1.h \
SpookyHashV2.h \
ssl/OpenSSLHash.h \
+ ssl/SSLSession.h \
+ ssl/detail/OpenSSLVersionFinder.h \
+ ssl/detail/SSLSessionImpl.h \
stats/BucketedTimeSeries-defs.h \
stats/BucketedTimeSeries.h \
stats/Histogram-defs.h \
SpookyHashV1.cpp \
SpookyHashV2.cpp \
ssl/OpenSSLHash.cpp \
+ ssl/detail/SSLSessionImpl.cpp \
stats/Instantiations.cpp \
Subprocess.cpp \
ThreadCachedArena.cpp \
--- /dev/null
+/*
+ * 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/io/async/test/AsyncSSLSocketTest.h>
+#include <folly/portability/GTest.h>
+#include <folly/portability/Sockets.h>
+#include <folly/portability/Unistd.h>
+#include <folly/ssl/SSLSession.h>
+
+using namespace std;
+using namespace testing;
+using folly::ssl::SSLSession;
+
+namespace folly {
+
+const char* testCert = "folly/io/async/test/certs/tests-cert.pem";
+const char* testKey = "folly/io/async/test/certs/tests-key.pem";
+const char* testCA = "folly/io/async/test/certs/ca-cert.pem";
+
+void getfds(int fds[2]) {
+ if (socketpair(PF_LOCAL, SOCK_STREAM, 0, fds) != 0) {
+ LOG(ERROR) << "failed to create socketpair: " << strerror(errno);
+ }
+ for (int idx = 0; idx < 2; ++idx) {
+ int flags = fcntl(fds[idx], F_GETFL, 0);
+ if (flags == -1) {
+ LOG(ERROR) << "failed to get flags for socket " << idx << ": "
+ << strerror(errno);
+ }
+ if (fcntl(fds[idx], F_SETFL, flags | O_NONBLOCK) != 0) {
+ LOG(ERROR) << "failed to put socket " << idx
+ << " in non-blocking mode: " << strerror(errno);
+ }
+ }
+}
+
+void getctx(
+ std::shared_ptr<folly::SSLContext> clientCtx,
+ std::shared_ptr<folly::SSLContext> serverCtx) {
+ clientCtx->ciphers("ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH");
+
+ serverCtx->ciphers("ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH");
+ serverCtx->loadCertificate(testCert);
+ serverCtx->loadPrivateKey(testKey);
+}
+
+class SSLSessionTest : public testing::Test {
+ public:
+ void SetUp() override {
+ clientCtx.reset(new folly::SSLContext());
+ dfServerCtx.reset(new folly::SSLContext());
+ hskServerCtx.reset(new folly::SSLContext());
+ serverName = "xyz.newdev.facebook.com";
+ getctx(clientCtx, dfServerCtx);
+ }
+
+ void TearDown() override {}
+
+ folly::EventBase eventBase;
+ std::shared_ptr<SSLContext> clientCtx;
+ std::shared_ptr<SSLContext> dfServerCtx;
+ // Use the same SSLContext to continue the handshake after
+ // tlsext_hostname match.
+ std::shared_ptr<SSLContext> hskServerCtx;
+ std::string serverName;
+};
+
+/**
+ * 1. Client sends TLSEXT_HOSTNAME in client hello.
+ * 2. Server found a match SSL_CTX and use this SSL_CTX to
+ * continue the SSL handshake.
+ * 3. Server sends back TLSEXT_HOSTNAME in server hello.
+ */
+TEST_F(SSLSessionTest, BasicTest) {
+ std::unique_ptr<SSLSession> sess;
+
+ {
+ int fds[2];
+ getfds(fds);
+ AsyncSSLSocket::UniquePtr clientSock(
+ new AsyncSSLSocket(clientCtx, &eventBase, fds[0], serverName));
+ auto clientPtr = clientSock.get();
+ AsyncSSLSocket::UniquePtr serverSock(
+ new AsyncSSLSocket(dfServerCtx, &eventBase, fds[1], true));
+ SSLHandshakeClient client(std::move(clientSock), false, false);
+ SSLHandshakeServerParseClientHello server(
+ std::move(serverSock), false, false);
+
+ eventBase.loop();
+ ASSERT_TRUE(client.handshakeSuccess_);
+
+ sess.reset(new SSLSession(clientPtr->getSSLSession()));
+ ASSERT_NE(sess.get(), nullptr);
+ }
+
+ {
+ int fds[2];
+ getfds(fds);
+ AsyncSSLSocket::UniquePtr clientSock(
+ new AsyncSSLSocket(clientCtx, &eventBase, fds[0], serverName));
+ auto clientPtr = clientSock.get();
+ clientSock->setSSLSession(sess->getRawSSLSessionDangerous(), true);
+ AsyncSSLSocket::UniquePtr serverSock(
+ new AsyncSSLSocket(dfServerCtx, &eventBase, fds[1], true));
+ SSLHandshakeClient client(std::move(clientSock), false, false);
+ SSLHandshakeServerParseClientHello server(
+ std::move(serverSock), false, false);
+
+ eventBase.loop();
+ ASSERT_TRUE(client.handshakeSuccess_);
+ ASSERT_TRUE(clientPtr->getSSLSessionReused());
+ }
+}
+TEST_F(SSLSessionTest, SerializeDeserializeTest) {
+ std::string sessiondata;
+
+ {
+ int fds[2];
+ getfds(fds);
+ AsyncSSLSocket::UniquePtr clientSock(
+ new AsyncSSLSocket(clientCtx, &eventBase, fds[0], serverName));
+ auto clientPtr = clientSock.get();
+ AsyncSSLSocket::UniquePtr serverSock(
+ new AsyncSSLSocket(dfServerCtx, &eventBase, fds[1], true));
+ SSLHandshakeClient client(std::move(clientSock), false, false);
+ SSLHandshakeServerParseClientHello server(
+ std::move(serverSock), false, false);
+
+ eventBase.loop();
+ ASSERT_TRUE(client.handshakeSuccess_);
+
+ std::unique_ptr<SSLSession> sess =
+ folly::make_unique<SSLSession>(clientPtr->getSSLSession());
+ sessiondata = sess->serialize();
+ ASSERT_TRUE(!sessiondata.empty());
+ }
+
+ {
+ int fds[2];
+ getfds(fds);
+ AsyncSSLSocket::UniquePtr clientSock(
+ new AsyncSSLSocket(clientCtx, &eventBase, fds[0], serverName));
+ auto clientPtr = clientSock.get();
+ std::unique_ptr<SSLSession> sess =
+ folly::make_unique<SSLSession>(sessiondata);
+ ASSERT_NE(sess.get(), nullptr);
+ clientSock->setSSLSession(sess->getRawSSLSessionDangerous(), true);
+ AsyncSSLSocket::UniquePtr serverSock(
+ new AsyncSSLSocket(dfServerCtx, &eventBase, fds[1], true));
+ SSLHandshakeClient client(std::move(clientSock), false, false);
+ SSLHandshakeServerParseClientHello server(
+ std::move(serverSock), false, false);
+
+ eventBase.loop();
+ ASSERT_TRUE(client.handshakeSuccess_);
+ ASSERT_TRUE(clientPtr->getSSLSessionReused());
+ }
+}
+
+TEST_F(SSLSessionTest, GetSessionID) {
+ int fds[2];
+ getfds(fds);
+ AsyncSSLSocket::UniquePtr clientSock(
+ new AsyncSSLSocket(clientCtx, &eventBase, fds[0], serverName));
+ auto clientPtr = clientSock.get();
+ AsyncSSLSocket::UniquePtr serverSock(
+ new AsyncSSLSocket(dfServerCtx, &eventBase, fds[1], true));
+ SSLHandshakeClient client(std::move(clientSock), false, false);
+ SSLHandshakeServerParseClientHello server(
+ std::move(serverSock), false, false);
+
+ eventBase.loop();
+ ASSERT_TRUE(client.handshakeSuccess_);
+
+ std::unique_ptr<SSLSession> sess =
+ folly::make_unique<SSLSession>(clientPtr->getSSLSession());
+ ASSERT_NE(sess, nullptr);
+ auto sessID = sess->getSessionID();
+ ASSERT_GE(sessID.length(), 0);
+}
+}
--- /dev/null
+/*
+ * 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 <folly/Memory.h>
+#include <folly/ssl/detail/OpenSSLVersionFinder.h>
+#include <folly/ssl/detail/SSLSessionImpl.h>
+
+namespace folly {
+namespace ssl {
+
+class SSLSession {
+ public:
+ // Holds and takes ownership of an SSL_SESSION object by incrementing refcount
+ explicit SSLSession(SSL_SESSION* session, bool takeOwnership = true)
+ : impl_(folly::make_unique<detail::SSLSessionImpl>(
+ session,
+ takeOwnership)) {}
+
+ // Deserialize from a string
+ explicit SSLSession(const std::string& serializedSession)
+ : impl_(folly::make_unique<detail::SSLSessionImpl>(serializedSession)) {}
+
+ // Serialize to a string that is suitable to store in a persistent cache
+ std::string serialize() const {
+ return impl_->serialize();
+ }
+
+ // Get Session ID. Returns an empty container if session isn't set
+ std::string getSessionID() const {
+ return impl_->getSessionID();
+ }
+
+ // Get a const raw SSL_SESSION ptr without incrementing referecnce count
+ // (Warning: do not use)
+ const SSL_SESSION* getRawSSLSession() const {
+ return impl_->getRawSSLSession();
+ }
+
+ // Get raw SSL_SESSION pointer
+ // Warning: do not use unless you know what you're doing - caller needs to
+ // decrement refcount using SSL_SESSION_free or this will leak
+ SSL_SESSION* getRawSSLSessionDangerous() {
+ return impl_->getRawSSLSessionDangerous();
+ }
+
+ private:
+ std::unique_ptr<detail::SSLSessionImpl> impl_;
+};
+
+} // namespace ssl
+} // namespace folly
--- /dev/null
+/*
+ * 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 <folly/Conv.h>
+
+#include <openssl/crypto.h>
+#include <openssl/opensslv.h>
+
+#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)
+
+// 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
+struct OpenSSLVersionFinder {
+ static std::string getOpenSSLLongVersion(void) {
+#ifdef OPENSSL_VERSION_TEXT
+ return SSLeay_version(SSLEAY_VERSION);
+#elif defined(OPENSSL_VERSION_NUMBER)
+ return folly::format("0x{:x}", OPENSSL_VERSION_NUMBER).str();
+#else
+ return "";
+#endif
+ }
+
+ uint64_t getOpenSSLNumericVersion(void) {
+#ifdef OPENSSL_VERSION_NUMBER
+ return SSLeay();
+#else
+ return 0;
+#endif
+ }
+};
--- /dev/null
+/*
+ * 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/ssl/detail/SSLSessionImpl.h>
+#include <folly/ssl/detail/OpenSSLVersionFinder.h>
+
+namespace folly {
+namespace ssl {
+namespace detail {
+
+//
+// Wrapper OpenSSL 1.0.2 (and possibly 1.0.1)
+//
+
+SSLSessionImpl::SSLSessionImpl(SSL_SESSION* session, bool takeOwnership)
+ : session_(session) {
+ if (session_ == nullptr) {
+ throw std::runtime_error("SSL_SESSION is null");
+ }
+ // If we're not given ownership, we need to up the refcount so the SSL_SESSION
+ // object won't be freed while SSLSessionImpl is alive
+ if (!takeOwnership) {
+ upRef();
+ }
+}
+
+SSLSessionImpl::SSLSessionImpl(const std::string& serializedSession) {
+ auto sessionData =
+ reinterpret_cast<const unsigned char*>(serializedSession.data());
+ if ((session_ = d2i_SSL_SESSION(
+ nullptr, &sessionData, serializedSession.length())) == nullptr) {
+ throw std::runtime_error("Cannot deserialize SSLSession string");
+ }
+}
+
+SSLSessionImpl::~SSLSessionImpl() {
+ downRef();
+}
+
+std::string SSLSessionImpl::serialize(void) const {
+ std::string ret;
+
+ // Get the length first, then we know how much space to allocate.
+ auto len = i2d_SSL_SESSION(session_, nullptr);
+
+ if (len > 0) {
+ std::unique_ptr<unsigned char[]> uptr(new unsigned char[len]);
+ auto p = uptr.get();
+ auto written = i2d_SSL_SESSION(session_, &p);
+ if (written <= 0) {
+ VLOG(2) << "Could not serialize SSL_SESSION!";
+ } else {
+ ret.assign(uptr.get(), uptr.get() + written);
+ }
+ }
+ return ret;
+}
+
+std::string SSLSessionImpl::getSessionID() const {
+ std::string ret;
+ 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;
+}
+
+const SSL_SESSION* SSLSessionImpl::getRawSSLSession() const {
+ return const_cast<SSL_SESSION*>(session_);
+}
+
+SSL_SESSION* SSLSessionImpl::getRawSSLSessionDangerous() {
+ upRef();
+ return session_;
+}
+
+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
+ }
+}
+
+void SSLSessionImpl::downRef() {
+ if (session_) {
+ SSL_SESSION_free(session_);
+ }
+}
+
+} // namespace detail
+} // namespace ssl
+} // namespace folly
--- /dev/null
+/*
+ * 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 <folly/Range.h>
+#include <openssl/ssl.h>
+#include <string>
+
+namespace folly {
+namespace ssl {
+namespace detail {
+
+class SSLSessionImpl {
+ public:
+ explicit SSLSessionImpl(SSL_SESSION* session, bool takeOwnership = true);
+ explicit SSLSessionImpl(const std::string& serializedSession);
+ virtual ~SSLSessionImpl();
+ std::string serialize() const;
+ std::string getSessionID() const;
+ const SSL_SESSION* getRawSSLSession() const;
+ SSL_SESSION* getRawSSLSessionDangerous();
+
+ private:
+ void upRef();
+ void downRef();
+
+ SSL_SESSION* session_{nullptr};
+};
+
+} // namespace detail
+} // namespace ssl
+} // namespace folly