Commit 485c56f6 authored by Dominik Charousset's avatar Dominik Charousset

Fix compatibility with OpenSSL < 1.1

parent ac91378e
......@@ -36,7 +36,6 @@ caf_add_component(
src/net/datagram_socket.cpp
src/net/generic_lower_layer.cpp
src/net/generic_upper_layer.cpp
src/net/host.cpp
src/net/http/header.cpp
src/net/http/lower_layer.cpp
src/net/http/method.cpp
......@@ -59,6 +58,7 @@ caf_add_component(
src/net/ssl/context.cpp
src/net/ssl/dtls.cpp
src/net/ssl/format.cpp
src/net/ssl/startup.cpp
src/net/ssl/tls.cpp
src/net/ssl/transport.cpp
src/net/stream_oriented.cpp
......@@ -66,6 +66,7 @@ caf_add_component(
src/net/stream_transport.cpp
src/net/tcp_accept_socket.cpp
src/net/tcp_stream_socket.cpp
src/net/this_host.cpp
src/net/udp_datagram_socket.cpp
src/net/web_socket/client.cpp
src/net/web_socket/default_trait.cpp
......
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#include "caf/detail/net_export.hpp"
#include "caf/fwd.hpp"
namespace caf::net::ssl {
/// Initializes the SSL layer. Depending on the version, this may be mandatory
/// to call before accessing any SSL functions (OpenSSL prior to version 1.1) or
/// it may have no effect (newer versions of OpenSSL).
CAF_NET_EXPORT void startup();
/// Cleans up any state for the SSL layer. Like @ref startup, this step is
/// mandatory for some versions of the linked SSL library.
CAF_NET_EXPORT void cleanup();
} // namespace caf::net::ssl
......@@ -36,4 +36,7 @@ bool inspect(Inspector& f, tls& x) {
/// @relates tls
CAF_NET_EXPORT int native(tls);
/// @relates tls
CAF_NET_EXPORT bool has(tls, tls, tls);
} // namespace caf::net::ssl
......@@ -13,7 +13,7 @@ namespace caf::net {
class CAF_NET_EXPORT this_host {
public:
/// Initializes the network subsystem.
static error startup();
static void startup();
/// Release any resources of the network subsystem.
static void cleanup();
......
......@@ -80,12 +80,18 @@ errc connection::last_error(ptrdiff_t ret) const {
return errc::want_accept;
case SSL_ERROR_WANT_X509_LOOKUP:
return errc::want_x509_lookup;
#ifdef SSL_ERROR_WANT_ASYNC
case SSL_ERROR_WANT_ASYNC:
return errc::want_async;
#endif
#ifdef SSL_ERROR_WANT_ASYNC_JOB
case SSL_ERROR_WANT_ASYNC_JOB:
return errc::want_async_job;
#endif
#ifdef SSL_ERROR_WANT_CLIENT_HELLO_CB
case SSL_ERROR_WANT_CLIENT_HELLO_CB:
return errc::want_client_hello;
#endif
case SSL_ERROR_SYSCALL:
return errc::syscall_failed;
case SSL_ERROR_SSL:
......
......@@ -45,23 +45,56 @@ context::~context() {
namespace {
std::pair<SSL_CTX*, const char*> make_ctx(const SSL_METHOD* method, int vmin,
int vmax) {
#if OPENSSL_VERSION_NUMBER < 0x10100000L
# define CAF_TLS_METHOD(infix) SSLv23##infix##method()
#else
# define CAF_TLS_METHOD(infix) TLS##infix##method()
#endif
template <class Enum>
std::pair<SSL_CTX*, const char*>
make_ctx(const SSL_METHOD* method, Enum min_val, Enum max_val) {
if (min_val > max_val && max_val != Enum::any)
return {nullptr, "invalid version range"};
auto ctx = SSL_CTX_new(method);
if (!ctx)
return {nullptr, "SSL_CTX_new returned null"};
// Avoid fallback to SSLv3.
// Select the protocol versions in the configured range.
#if OPENSSL_VERSION_NUMBER < 0x10100000L
if constexpr (std::is_same_v<Enum, tls>) {
auto opts = SSL_OP_NO_SSLv3;
if (!has(tls::v1_0, min_val, max_val))
opts |= SSL_OP_NO_TLSv1;
if (!has(tls::v1_1, min_val, max_val))
opts |= SSL_OP_NO_TLSv1_1;
if (!has(tls::v1_2, min_val, max_val))
opts |= SSL_OP_NO_TLSv1_2;
SSL_CTX_set_options(ctx, opts);
} else {
static_assert(std::is_same_v<Enum, dtls>);
// Nothing to do for DTLS in this OpenSSL version.
CAF_IGNORE_UNUSED(min_val);
CAF_IGNORE_UNUSED(max_val);
}
#else
if constexpr (std::is_same_v<Enum, tls>) {
SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv3);
}
auto vmin = native(min_val);
auto vmax = native(max_val);
if (vmin != 0 && SSL_CTX_set_min_proto_version(ctx, vmin) != 1)
return {ctx, "SSL_CTX_set_min_proto_version returned an error"};
if (vmax != 0 && SSL_CTX_set_max_proto_version(ctx, vmax) != 1)
return {ctx, "SSL_CTX_set_max_proto_version returned an error"};
#endif
return {ctx, nullptr};
}
} // namespace
expected<context> context::make(tls min_version, tls max_version) {
auto [raw, errstr] = make_ctx(TLS_method(), native(min_version),
native(max_version));
expected<context> context::make(tls vmin, tls vmax) {
auto [raw, errstr] = make_ctx(CAF_TLS_METHOD(_), vmin, vmax);
context ctx{reinterpret_cast<impl*>(raw)};
if (errstr == nullptr)
return {std::move(ctx)};
......@@ -69,9 +102,8 @@ expected<context> context::make(tls min_version, tls max_version) {
return {make_error(sec::logic_error, errstr)};
}
expected<context> context::make_server(tls min_version, tls max_version) {
auto [raw, errstr] = make_ctx(TLS_server_method(), native(min_version),
native(max_version));
expected<context> context::make_server(tls vmin, tls vmax) {
auto [raw, errstr] = make_ctx(CAF_TLS_METHOD(_server_), vmin, vmax);
context ctx{reinterpret_cast<impl*>(raw)};
if (errstr == nullptr)
return {std::move(ctx)};
......@@ -79,9 +111,8 @@ expected<context> context::make_server(tls min_version, tls max_version) {
return {make_error(sec::logic_error, errstr)};
}
expected<context> context::make_client(tls min_version, tls max_version) {
auto [raw, errstr] = make_ctx(TLS_client_method(), native(min_version),
native(max_version));
expected<context> context::make_client(tls vmin, tls vmax) {
auto [raw, errstr] = make_ctx(CAF_TLS_METHOD(_client_), vmin, vmax);
context ctx{reinterpret_cast<impl*>(raw)};
if (errstr == nullptr)
return {std::move(ctx)};
......@@ -89,9 +120,8 @@ expected<context> context::make_client(tls min_version, tls max_version) {
return {make_error(sec::logic_error, errstr)};
}
expected<context> context::make(dtls min_version, dtls max_version) {
auto [raw, errstr] = make_ctx(TLS_method(), native(min_version),
native(max_version));
expected<context> context::make(dtls vmin, dtls vmax) {
auto [raw, errstr] = make_ctx(DTLS_method(), vmin, vmax);
context ctx{reinterpret_cast<impl*>(raw)};
if (errstr == nullptr)
return {std::move(ctx)};
......@@ -99,9 +129,8 @@ expected<context> context::make(dtls min_version, dtls max_version) {
return {make_error(sec::logic_error, errstr)};
}
expected<context> context::make_server(dtls min_version, dtls max_version) {
auto [raw, errstr] = make_ctx(DTLS_server_method(), native(min_version),
native(max_version));
expected<context> context::make_server(dtls vmin, dtls vmax) {
auto [raw, errstr] = make_ctx(DTLS_server_method(), vmin, vmax);
context ctx{reinterpret_cast<impl*>(raw)};
if (errstr == nullptr)
return {std::move(ctx)};
......@@ -109,9 +138,8 @@ expected<context> context::make_server(dtls min_version, dtls max_version) {
return {make_error(sec::logic_error, errstr)};
}
expected<context> context::make_client(dtls min_version, dtls max_version) {
auto [raw, errstr] = make_ctx(DTLS_client_method(), native(min_version),
native(max_version));
expected<context> context::make_client(dtls vmin, dtls vmax) {
auto [raw, errstr] = make_ctx(DTLS_client_method(), vmin, vmax);
context ctx{reinterpret_cast<impl*>(raw)};
if (errstr == nullptr)
return {std::move(ctx)};
......
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#include "caf/net/ssl/startup.hpp"
#include "caf/config.hpp"
CAF_PUSH_WARNINGS
#include <openssl/err.h>
#include <openssl/ssl.h>
CAF_POP_WARNINGS
#if OPENSSL_VERSION_NUMBER < 0x10100000L
# include <mutex>
# include <vector>
struct CRYPTO_dynlock_value {
std::mutex mtx;
};
namespace {
std::vector<std::mutex> mutexes;
void locking_function(int mode, int n, const char*, int) {
if (mode & CRYPTO_LOCK)
mutexes[static_cast<size_t>(n)].lock();
else
mutexes[static_cast<size_t>(n)].unlock();
}
CRYPTO_dynlock_value* dynlock_create(const char*, int) {
return new CRYPTO_dynlock_value;
}
void dynlock_lock(int mode, CRYPTO_dynlock_value* dynlock, const char*, int) {
if (mode & CRYPTO_LOCK)
dynlock->mtx.lock();
else
dynlock->mtx.unlock();
}
void dynlock_destroy(CRYPTO_dynlock_value* dynlock, const char*, int) {
delete dynlock;
}
} // namespace
#endif
namespace caf::net::ssl {
void startup() {
#if OPENSSL_VERSION_NUMBER < 0x10100000L
SSL_library_init();
SSL_load_error_strings();
OpenSSL_add_all_algorithms();
mutexes = std::vector<std::mutex>{static_cast<size_t>(CRYPTO_num_locks())};
CRYPTO_set_locking_callback(locking_function);
CRYPTO_set_dynlock_create_callback(dynlock_create);
CRYPTO_set_dynlock_lock_callback(dynlock_lock);
CRYPTO_set_dynlock_destroy_callback(dynlock_destroy);
#endif
}
void cleanup() {
#if OPENSSL_VERSION_NUMBER < 0x10100000L
CRYPTO_set_locking_callback(nullptr);
CRYPTO_set_dynlock_create_callback(nullptr);
CRYPTO_set_dynlock_lock_callback(nullptr);
CRYPTO_set_dynlock_destroy_callback(nullptr);
mutexes.clear();
#endif
}
} // namespace caf::net::ssl
......@@ -22,9 +22,16 @@ int native(tls x) {
return TLS1_1_VERSION;
case tls::v1_2:
return TLS1_2_VERSION;
#ifdef TLS1_3_VERSION
case tls::v1_3:
return TLS1_3_VERSION;
#endif
}
}
bool has(tls val, tls vmin, tls vmax) {
CAF_ASSERT(val != tls::any);
return val >= vmin && (vmax == tls::any || val <= vmax);
}
} // namespace caf::net::ssl
......@@ -2,7 +2,7 @@
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#include "caf/net/host.hpp"
#include "caf/net/this_host.hpp"
#include "caf/config.hpp"
#include "caf/detail/net_syscall.hpp"
......@@ -16,11 +16,10 @@ namespace caf::net {
#ifdef CAF_WINDOWS
error this_host::startup() {
void this_host::startup() {
WSADATA WinsockData;
CAF_NET_SYSCALL("WSAStartup", result, !=, 0,
WSAStartup(MAKEWORD(2, 2), &WinsockData));
return none;
CAF_NET_CRITICAL_SYSCALL("WSAStartup", result, !=, 0,
WSAStartup(MAKEWORD(2, 2), &WinsockData));
}
void this_host::cleanup() {
......@@ -29,8 +28,8 @@ void this_host::cleanup() {
#else // CAF_WINDOWS
error this_host::startup() {
return none;
void this_host::startup() {
// nop
}
void this_host::cleanup() {
......
......@@ -2,8 +2,9 @@
#include "caf/error.hpp"
#include "caf/init_global_meta_objects.hpp"
#include "caf/net/host.hpp"
#include "caf/net/middleman.hpp"
#include "caf/net/ssl/startup.hpp"
#include "caf/net/this_host.hpp"
#include "caf/raise_error.hpp"
#define CAF_TEST_NO_MAIN
......@@ -90,12 +91,13 @@ void barrier::arrive_and_wait() {
// -- main --------------------------------------------------------------------
int main(int argc, char** argv) {
if (auto err = net::this_host::startup())
CAF_RAISE_ERROR(std::logic_error, "this_host::startup failed");
net::this_host::startup();
net::ssl::startup();
using namespace caf;
net::middleman::init_global_meta_objects();
core::init_global_meta_objects();
auto result = test::main(argc, argv);
net::ssl::cleanup();
net::this_host::cleanup();
return result;
}
......@@ -34,7 +34,6 @@ struct fixture {
fixture() {
multiplexer::block_sigpipe();
OPENSSL_init_ssl(OPENSSL_INIT_SSL_DEFAULT, nullptr);
}
template <class SocketPair>
......
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