improve support for SSL connections. make passing in the SSL context into the http_connection mandatory (simplifying it, so it doesn't have to create its own). Sepaate the SSL context used for trackers and SSL torrents, so normal trackers can be validated, without interfering with the special SNI callback used for SSL torrents

This commit is contained in:
arvidn 2020-03-13 12:15:39 +01:00 committed by Arvid Norberg
parent b1a3782264
commit 4fd6136b2a
21 changed files with 277 additions and 95 deletions

View File

@ -320,6 +320,7 @@ set(sources
instantiate_connection
merkle
natpmp
openssl
part_file
packet_buffer
piece_picker

View File

@ -1,3 +1,4 @@
* support validation of HTTPS trackers
* deprecate strict super seeding mode
* make UPnP port-mapping lease duration configurable
* deprecate the bittyrant choking algorithm

View File

@ -663,6 +663,7 @@ SOURCES =
instantiate_connection
lazy_bdecode
natpmp
openssl
packet_buffer
piece_picker
peer_list

View File

@ -559,3 +559,6 @@ leecher
6881l
NOTSENT
LOWAT
tls11
tls12
tls13

View File

@ -51,7 +51,6 @@ POSSIBILITY OF SUCH DAMAGE.
#ifdef TORRENT_USE_OPENSSL
// all of OpenSSL causes warnings, so we just have to disable them
#include "libtorrent/aux_/disable_warnings_push.hpp"
#ifdef TORRENT_WINDOWS
@ -63,43 +62,50 @@ POSSIBILITY OF SUCH DAMAGE.
#include <openssl/safestack.h> // for sk_GENERAL_NAME_value
#include <openssl/x509v3.h> // for GENERAL_NAME
namespace libtorrent { namespace aux {
inline void openssl_set_tlsext_hostname(SSL* s, char const* name)
{
#if OPENSSL_VERSION_NUMBER >= 0x90812f
SSL_set_tlsext_host_name(s, name);
#include <boost/asio/ssl.hpp>
#if defined TORRENT_BUILD_SIMULATOR
#include "simulator/simulator.hpp"
#endif
}
#include "libtorrent/aux_/disable_warnings_pop.hpp"
namespace libtorrent {
namespace ssl {
#if defined TORRENT_BUILD_SIMULATOR
using sim::asio::ssl::context;
using sim::asio::ssl::stream_base;
using sim::asio::ssl::stream;
#else
using boost::asio::ssl::context;
using boost::asio::ssl::stream_base;
using boost::asio::ssl::stream;
#endif
} // ssl
namespace aux {
TORRENT_EXTRA_EXPORT void openssl_set_tlsext_hostname(SSL* s, char const* name);
#if OPENSSL_VERSION_NUMBER >= 0x90812f
inline void openssl_set_tlsext_servername_callback(SSL_CTX* ctx
, int (*servername_callback)(SSL*, int*, void*))
{
SSL_CTX_set_tlsext_servername_callback(ctx, servername_callback);
}
TORRENT_EXTRA_EXPORT void openssl_set_tlsext_servername_callback(SSL_CTX* ctx
, int (*servername_callback)(SSL*, int*, void*));
inline void openssl_set_tlsext_servername_arg(SSL_CTX* ctx, void* userdata)
{
SSL_CTX_set_tlsext_servername_arg(ctx, userdata);
}
TORRENT_EXTRA_EXPORT void openssl_set_tlsext_servername_arg(SSL_CTX* ctx, void* userdata);
inline int openssl_num_general_names(GENERAL_NAMES* gens)
{
return sk_GENERAL_NAME_num(gens);
}
TORRENT_EXTRA_EXPORT int openssl_num_general_names(GENERAL_NAMES* gens);
inline GENERAL_NAME* openssl_general_name_value(GENERAL_NAMES* gens, int i)
{
return sk_GENERAL_NAME_value(gens, i);
}
TORRENT_EXTRA_EXPORT GENERAL_NAME* openssl_general_name_value(GENERAL_NAMES* gens, int i);
#endif // OPENSSL_VERSION_NUMBER
}
}
// converts setting_pack::ssl_version_t enum into asio version
ssl::context::method ssl_version(int const v);
#include "libtorrent/aux_/disable_warnings_pop.hpp"
} // aux
} // libtorrent
#endif // TORRENT_USE_OPENSSL

View File

@ -876,9 +876,14 @@ namespace aux {
io_service& m_io_service;
#ifdef TORRENT_USE_OPENSSL
// this is a generic SSL context used when talking to
// unauthenticated HTTPS servers
// this is a generic SSL context used when talking to HTTPS servers
ssl::context m_ssl_ctx;
// this is the SSL context used for SSL listen sockets. It doesn't
// verify peers, but it has the servername callback set on it. Once it
// knows which torrent a peer is connecting to, it will switch the
// socket over to the torrent specific context, which does verify peers
ssl::context m_peer_ssl_ctx;
#endif
// handles delayed alerts

View File

@ -38,6 +38,7 @@ POSSIBILITY OF SUCH DAMAGE.
#define _FILE_OFFSET_BITS 64
#include <boost/config.hpp>
#include <boost/version.hpp>
#include "libtorrent/aux_/disable_warnings_pop.hpp"
@ -567,6 +568,14 @@ constexpr std::size_t TORRENT_WRITE_HANDLER_MAX_SIZE = 342;
#endif
#endif // TORRENT_HAS_ARM_CRC32
#ifndef TORRENT_USE_TLS13
#if BOOST_VERSION >= 106900
#define TORRENT_USE_TLS13 1
#else
#define TORRENT_USE_TLS13 0
#endif
#endif
namespace libtorrent {}
// create alias

View File

@ -82,12 +82,12 @@ struct TORRENT_EXTRA_EXPORT http_connection
http_connection(io_service& ios
, resolver_interface& resolver
, http_handler const& handler
, bool bottled = true
, int max_bottled_buffer_size = default_max_bottled_buffer_size
, http_connect_handler const& ch = http_connect_handler()
, http_filter_handler const& fh = http_filter_handler()
, bool bottled
, int max_bottled_buffer_size
, http_connect_handler const& ch
, http_filter_handler const& fh
#ifdef TORRENT_USE_OPENSSL
, ssl::context* ssl_ctx = nullptr
, ssl::context* ssl_ctx
#endif
);
@ -165,7 +165,6 @@ private:
#ifdef TORRENT_USE_OPENSSL
ssl::context* m_ssl_ctx;
bool m_own_ssl_context;
#endif
#if TORRENT_USE_I2P

View File

@ -1746,6 +1746,12 @@ namespace aux {
// lower than 5 minutes.
upnp_lease_duration,
// the SSL/TLS version to use for HTTPS trackers and SSL torrents. Set
// it to one of the ssl_version_t values. This setting only takes
// effect when passed in to the session constructor. It cannot be
// changed once the session has been constructed.
ssl_version,
max_int_setting_internal
};
@ -1759,6 +1765,18 @@ namespace aux {
enum suggest_mode_t : std::uint8_t { no_piece_suggestions = 0, suggest_read_cache = 1 };
enum ssl_version_t : std::uint8_t
{
// TLS version 1.1
tls11,
// TLS version 1.2
tls12,
#if TORRENT_USE_TLS13
// TLS version 1.3
tls13,
#endif
};
enum choking_algorithm_t : std::uint8_t
{
fixed_slots_choker = 0,

View File

@ -42,28 +42,8 @@ POSSIBILITY OF SUCH DAMAGE.
#include <functional>
#include "libtorrent/aux_/disable_warnings_push.hpp"
#include <boost/asio/ssl.hpp>
#if defined TORRENT_BUILD_SIMULATOR
#include "simulator/simulator.hpp"
#endif
#include "libtorrent/aux_/disable_warnings_pop.hpp"
namespace libtorrent {
namespace ssl {
#if defined TORRENT_BUILD_SIMULATOR
using sim::asio::ssl::context;
using sim::asio::ssl::stream_base;
using sim::asio::ssl::stream;
#else
using boost::asio::ssl::context;
using boost::asio::ssl::stream_base;
using boost::asio::ssl::stream;
#endif
}
template <class Stream>
class ssl_stream
{

View File

@ -43,6 +43,7 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/aux_/portmap.hpp"
#include "libtorrent/aux_/vector.hpp"
#include "libtorrent/aux_/session_settings.hpp"
#include "libtorrent/aux_/openssl.hpp" // for ssl::context
#include <memory>
#include <functional>
@ -357,6 +358,10 @@ private:
address_v4 m_listen_address;
address_v4 m_netmask;
std::string m_device;
#ifdef TORRENT_USE_OPENSSL
ssl::context m_ssl_ctx;
#endif
};
}

View File

@ -131,6 +131,11 @@ std::shared_ptr<http_connection> test_request(io_service& ios
{
std::printf(" ===== TESTING: %s =====\n", url.c_str());
#ifdef TORRENT_USE_OPENSSL
ssl::context ssl_ctx(ssl::context::sslv23_client);
ssl_ctx.set_verify_mode(ssl::context::verify_none);
#endif
auto h = std::make_shared<http_connection>(ios
, res
, [=](error_code const& ec, http_parser const& parser
@ -177,7 +182,12 @@ std::shared_ptr<http_connection> test_request(io_service& ios
++*connect_handler_called;
TEST_CHECK(c.socket().is_open());
std::printf("CONNECTED: %s\n", url.c_str());
});
}
, lt::http_filter_handler()
#ifdef TORRENT_USE_OPENSSL
, &ssl_ctx
#endif
);
h->get(url, seconds(1), 0, &ps, 5, "test/user-agent", boost::none
, resolver_flags{}, auth);
@ -630,14 +640,25 @@ TORRENT_TEST(http_connection_ssl_proxy)
return sim::send_response(403, "Not supported", 1337);
});
#ifdef TORRENT_USE_OPENSSL
lt::ssl::context ssl_ctx(ssl::context::sslv23_client);
ssl_ctx.set_verify_mode(ssl::context::verify_none);
#endif
auto h = std::make_shared<http_connection>(client_ios
, res
, [&client_counter](error_code const& ec, http_parser const&
, span<char const>, http_connection&)
, span<char const>, http_connection&)
{
client_counter++;
TEST_EQUAL(ec, boost::asio::error::operation_not_supported);
});
}
, true, 1024*1024, lt::http_connect_handler()
, http_filter_handler()
#ifdef TORRENT_USE_OPENSSL
, &ssl_ctx
#endif
);
h->start("10.0.0.2", 8080, seconds(1), 0, &ps, true /*ssl*/);

View File

@ -94,6 +94,7 @@ libtorrent_rasterbar_la_SOURCES = \
magnet_uri.cpp \
merkle.cpp \
natpmp.cpp \
openssl.cpp \
parse_url.cpp \
part_file.cpp \
pe_crypto.cpp \

View File

@ -74,7 +74,6 @@ http_connection::http_connection(io_service& ios
, m_sock(ios)
#ifdef TORRENT_USE_OPENSSL
, m_ssl_ctx(ssl_ctx)
, m_own_ssl_context(false)
#endif
#if TORRENT_USE_I2P
, m_i2p_conn(nullptr)
@ -107,12 +106,7 @@ http_connection::http_connection(io_service& ios
TORRENT_ASSERT(m_handler);
}
http_connection::~http_connection()
{
#ifdef TORRENT_USE_OPENSSL
if (m_own_ssl_context) delete m_ssl_ctx;
#endif
}
http_connection::~http_connection() = default;
void http_connection::get(std::string const& url, time_duration timeout, int prio
, aux::proxy_settings const* ps, int handle_redirects, std::string const& user_agent
@ -256,6 +250,10 @@ void http_connection::start(std::string const& hostname, int port
m_read_pos = 0;
m_priority = prio;
#ifdef TORRENT_USE_OPENSSL
TORRENT_ASSERT(!ssl || m_ssl_ctx != nullptr);
#endif
if (ec)
{
lt::get_io_service(m_timer).post(std::bind(&http_connection::callback
@ -323,21 +321,7 @@ void http_connection::start(std::string const& hostname, int port
#ifdef TORRENT_USE_OPENSSL
if (m_ssl)
{
if (m_ssl_ctx == nullptr)
{
m_ssl_ctx = new (std::nothrow) ssl::context(ssl::context::sslv23_client);
if (m_ssl_ctx)
{
m_own_ssl_context = true;
m_ssl_ctx->set_verify_mode(ssl::context::verify_none, ec);
if (ec)
{
lt::get_io_service(m_timer).post(std::bind(&http_connection::callback
, me, ec, span<char>{}));
return;
}
}
}
TORRENT_ASSERT(m_ssl_ctx != nullptr);
userdata = m_ssl_ctx;
}
#endif

97
src/openssl.cpp Normal file
View File

@ -0,0 +1,97 @@
/*
Copyright (c) 2020, Arvid Norberg
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#include "libtorrent/config.hpp"
#include "libtorrent/aux_/openssl.hpp"
#include "libtorrent/settings_pack.hpp"
namespace libtorrent {
namespace aux {
#ifdef TORRENT_USE_OPENSSL
// all of OpenSSL causes warnings, so we just have to disable them
#include "libtorrent/aux_/disable_warnings_push.hpp"
void openssl_set_tlsext_hostname(SSL* s, char const* name)
{
#if OPENSSL_VERSION_NUMBER >= 0x90812f
SSL_set_tlsext_host_name(s, name);
#endif
}
#if OPENSSL_VERSION_NUMBER >= 0x90812f
void openssl_set_tlsext_servername_callback(SSL_CTX* ctx
, int (*servername_callback)(SSL*, int*, void*))
{
SSL_CTX_set_tlsext_servername_callback(ctx, servername_callback);
}
void openssl_set_tlsext_servername_arg(SSL_CTX* ctx, void* userdata)
{
SSL_CTX_set_tlsext_servername_arg(ctx, userdata);
}
int openssl_num_general_names(GENERAL_NAMES* gens)
{
return sk_GENERAL_NAME_num(gens);
}
GENERAL_NAME* openssl_general_name_value(GENERAL_NAMES* gens, int i)
{
return sk_GENERAL_NAME_value(gens, i);
}
#include "libtorrent/aux_/disable_warnings_pop.hpp"
#endif // OPENSSL_VERSION_NUMBER
ssl::context::method ssl_version(int const v)
{
switch (v)
{
case settings_pack::tls11: return ssl::context::tlsv11;
case settings_pack::tls12: return ssl::context::tlsv12;
#if TORRENT_USE_TLS13
case settings_pack::tls13: return ssl::context::tlsv13;
default: return ssl::context::tlsv13;
#else
default: return ssl::context::tlsv12;
#endif
};
}
#endif // TORRENT_USE_OPENSSL
}
}

View File

@ -454,7 +454,8 @@ namespace aux {
: m_settings(pack)
, m_io_service(ios)
#ifdef TORRENT_USE_OPENSSL
, m_ssl_ctx(boost::asio::ssl::context::sslv23)
, m_ssl_ctx(ssl_version(pack.get_int(settings_pack::ssl_version)))
, m_peer_ssl_ctx(ssl_version(pack.get_int(settings_pack::ssl_version)))
#endif
, m_alerts(m_settings.get_int(settings_pack::alert_queue_size)
, alert_category_t{static_cast<unsigned int>(m_settings.get_int(settings_pack::alert_mask))})
@ -495,7 +496,7 @@ namespace aux {
, std::bind(&session_impl::on_incoming_utp_ssl, this, _1)
, m_io_service
, m_settings, m_stats_counters
, &m_ssl_ctx)
, &m_peer_ssl_ctx)
#endif
, m_timer(m_io_service)
, m_lsd_announce_timer(m_io_service)
@ -537,10 +538,11 @@ namespace aux {
#ifdef TORRENT_USE_OPENSSL
error_code ec;
m_ssl_ctx.set_verify_mode(boost::asio::ssl::context::verify_none, ec);
m_peer_ssl_ctx.set_verify_mode(boost::asio::ssl::context::verify_none, ec);
#if OPENSSL_VERSION_NUMBER >= 0x90812f
aux::openssl_set_tlsext_servername_callback(m_ssl_ctx.native_handle()
aux::openssl_set_tlsext_servername_callback(m_peer_ssl_ctx.native_handle()
, servername_callback);
aux::openssl_set_tlsext_servername_arg(m_ssl_ctx.native_handle(), this);
aux::openssl_set_tlsext_servername_arg(m_peer_ssl_ctx.native_handle(), this);
#endif // OPENSSL_VERSION_NUMBER
#endif
@ -2461,11 +2463,11 @@ namespace {
#ifdef TORRENT_USE_OPENSSL
if (ssl == transport::ssl)
{
// accept connections initializing the SSL connection to
// use the generic m_ssl_ctx context. However, since it has
// the servername callback set on it, we will switch away from
// this context into a specific torrent once we start handshaking
c->instantiate<ssl_stream<tcp::socket>>(m_io_service, &m_ssl_ctx);
// accept connections initializing the SSL connection to use the peer
// ssl context. Since it has the servername callback set on it, we will
// switch away from this context into a specific torrent once we start
// handshaking
c->instantiate<ssl_stream<tcp::socket>>(m_io_service, &m_peer_ssl_ctx);
str = &c->get<ssl_stream<tcp::socket>>()->next_layer();
}
else

View File

@ -350,6 +350,11 @@ constexpr int CLOSE_FILE_INTERVAL = 0;
SET(resolver_cache_timeout, 1200, &session_impl::update_resolver_cache_timeout),
SET(send_not_sent_low_watermark, 16384, nullptr),
SET(upnp_lease_duration, 3600, nullptr),
#if TORRENT_USE_TLS13
SET(ssl_version, settings_pack::tls13, nullptr),
#else
SET(ssl_version, settings_pack::tls12, nullptr),
#endif
}});
#undef SET

View File

@ -1625,7 +1625,8 @@ bool is_downloading_state(int const st)
// create the SSL context for this torrent. We need to
// inject the root certificate, and no other, to
// verify other peers against
std::shared_ptr<context> ctx = std::make_shared<context>(context::sslv23);
std::shared_ptr<context> ctx = std::make_shared<context>(
aux::ssl_version(settings().get_int(settings_pack::ssl_version)));
if (!ctx)
{

View File

@ -50,6 +50,9 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/aux_/disable_warnings_push.hpp"
#include <boost/asio/ip/host_name.hpp>
#include <boost/asio/ip/multicast.hpp>
#ifdef TORRENT_USE_OPENSSL
#include <boost/asio/ssl/context.hpp>
#endif
#include "libtorrent/aux_/disable_warnings_pop.hpp"
#include <cstdlib>
@ -114,7 +117,13 @@ upnp::upnp(io_service& ios
, m_listen_address(listen_address)
, m_netmask(netmask)
, m_device(std::move(listen_device))
#ifdef TORRENT_USE_OPENSSL
, m_ssl_ctx(ssl::context::sslv23_client)
#endif
{
#ifdef TORRENT_USE_OPENSSL
m_ssl_ctx.set_verify_mode(ssl::context::verify_none);
#endif
}
void upnp::start()
@ -420,7 +429,13 @@ void upnp::connect(rootdevice& d)
d.upnp_connection = std::make_shared<http_connection>(m_io_service
, m_resolver
, std::bind(&upnp::on_upnp_xml, self(), _1, _2
, std::ref(d), _4));
, std::ref(d), _4), true, default_max_bottled_buffer_size
, http_connect_handler()
, http_filter_handler()
#ifdef TORRENT_USE_OPENSSL
, &m_ssl_ctx
#endif
);
d.upnp_connection->get(d.url, seconds(30), 1);
}
TORRENT_CATCH (std::exception const& exc)
@ -819,7 +834,12 @@ void upnp::update_map(rootdevice& d, port_mapping_t const i)
, m_resolver
, std::bind(&upnp::on_upnp_map_response, self(), _1, _2
, std::ref(d), i, _4), true, default_max_bottled_buffer_size
, std::bind(&upnp::create_port_mapping, self(), _1, std::ref(d), i));
, std::bind(&upnp::create_port_mapping, self(), _1, std::ref(d), i)
, http_filter_handler()
#ifdef TORRENT_USE_OPENSSL
, &m_ssl_ctx
#endif
);
d.upnp_connection->start(d.hostname, d.port
, seconds(10), 1, nullptr, false, 5, m.local_ep.address());
@ -831,7 +851,12 @@ void upnp::update_map(rootdevice& d, port_mapping_t const i)
, m_resolver
, std::bind(&upnp::on_upnp_unmap_response, self(), _1, _2
, std::ref(d), i, _4), true, default_max_bottled_buffer_size
, std::bind(&upnp::delete_port_mapping, self(), std::ref(d), i));
, std::bind(&upnp::delete_port_mapping, self(), std::ref(d), i)
, http_filter_handler()
#ifdef TORRENT_USE_OPENSSL
, &m_ssl_ctx
#endif
);
d.upnp_connection->start(d.hostname, d.port
, seconds(10), 1, nullptr, false, 5, m.local_ep.address());
}
@ -1044,7 +1069,12 @@ void upnp::on_upnp_xml(error_code const& e
, m_resolver
, std::bind(&upnp::on_upnp_get_ip_address_response, self(), _1, _2
, std::ref(d), _4), true, default_max_bottled_buffer_size
, std::bind(&upnp::get_ip_address, self(), std::ref(d)));
, std::bind(&upnp::get_ip_address, self(), std::ref(d))
, http_filter_handler()
#ifdef TORRENT_USE_OPENSSL
, &m_ssl_ctx
#endif
);
d.upnp_connection->start(d.hostname, d.port
, seconds(10), 1);
}

View File

@ -87,6 +87,9 @@ lt::settings_pack settings()
pack.set_int(settings_pack::half_open_limit, 1);
#endif
// to be compatible with python
pack.set_int(settings_pack::ssl_version, settings_pack::tls12);
return pack;
}

View File

@ -121,8 +121,18 @@ void run_test(std::string const& url, int size, int status, int connected
<< " connected: " << connected
<< " error: " << (ec?ec->message():"no error") << std::endl;
#ifdef TORRENT_USE_OPENSSL
ssl::context ssl_ctx(ssl::context::sslv23_client);
ssl_ctx.set_verify_mode(ssl::context::verify_none);
#endif
std::shared_ptr<http_connection> h = std::make_shared<http_connection>(ios
, res, &::http_handler_test, true, 1024*1024, &::http_connect_handler_test);
, res, &::http_handler_test, true, 1024*1024, &::http_connect_handler_test
, http_filter_handler()
#ifdef TORRENT_USE_OPENSSL
, &ssl_ctx
#endif
);
h->get(url, seconds(5), 0, &ps, 5, "test/user-agent", boost::none, resolver_flags{}, auth);
ios.reset();
error_code e;