initial attempt at verifying the certificate of ssl trackers, by including the certificate in the .torrent file
This commit is contained in:
parent
e7185b519b
commit
a286a6b4aa
|
@ -85,6 +85,8 @@ void print_usage()
|
|||
" If this is not specified, the torrent file is\n"
|
||||
" printed to the standard out, except on windows\n"
|
||||
" where the filename defaults to a.torrent\n"
|
||||
"-c add root certificate to the torrent, to make\n"
|
||||
" it an SSL torrent\n"
|
||||
, stderr);
|
||||
}
|
||||
|
||||
|
@ -109,6 +111,7 @@ int main(int argc, char* argv[])
|
|||
int pad_file_limit = -1;
|
||||
int piece_size = 0;
|
||||
int flags = 0;
|
||||
std::string root_cert;
|
||||
|
||||
std::string outfile;
|
||||
std::string merklefile;
|
||||
|
@ -160,6 +163,10 @@ int main(int argc, char* argv[])
|
|||
case 'l':
|
||||
flags |= create_torrent::symlinks;
|
||||
break;
|
||||
case 'c':
|
||||
++i;
|
||||
root_cert = argv[i];
|
||||
break;
|
||||
default:
|
||||
print_usage();
|
||||
return 1;
|
||||
|
@ -198,6 +205,20 @@ int main(int argc, char* argv[])
|
|||
fprintf(stderr, "\n");
|
||||
t.set_creator(creator_str);
|
||||
|
||||
if (!root_cert.empty())
|
||||
{
|
||||
FILE* cert = fopen(root_cert.c_str(), "rb");
|
||||
if (cert)
|
||||
{
|
||||
std::string pem;
|
||||
pem.resize(5000);
|
||||
int s = fread(&pem[0], 1, pem.size(), cert);
|
||||
pem.resize(s);
|
||||
t.set_root_cert(pem);
|
||||
fclose(cert);
|
||||
}
|
||||
}
|
||||
|
||||
// create the torrent and print it to stdout
|
||||
std::vector<char> torrent;
|
||||
bencode(back_inserter(torrent), t.generate());
|
||||
|
|
|
@ -89,6 +89,7 @@ namespace libtorrent
|
|||
void add_http_seed(std::string const& url);
|
||||
void add_node(std::pair<std::string, int> const& node);
|
||||
void add_tracker(std::string const& url, int tier = 0);
|
||||
void set_root_cert(std::string const& pem);
|
||||
void set_priv(bool p) { m_private = p; }
|
||||
|
||||
int num_pieces() const { return m_files.num_pieces(); }
|
||||
|
@ -145,6 +146,9 @@ namespace libtorrent
|
|||
// to create the torrent file
|
||||
std::string m_created_by;
|
||||
|
||||
// this is the root cert for SSL torrents
|
||||
std::string m_root_cert;
|
||||
|
||||
// this is used when creating a torrent. If there's
|
||||
// only one file there are cases where it's impossible
|
||||
// to know if it should be written as a multifile torrent
|
||||
|
|
|
@ -78,40 +78,13 @@ struct TORRENT_EXPORT http_connection : boost::enable_shared_from_this<http_conn
|
|||
http_connection(io_service& ios, connection_queue& cc
|
||||
, http_handler const& handler, bool bottled = true
|
||||
, http_connect_handler const& ch = http_connect_handler()
|
||||
, http_filter_handler const& fh = http_filter_handler())
|
||||
: m_sock(ios)
|
||||
#if TORRENT_USE_I2P
|
||||
, m_i2p_conn(0)
|
||||
#endif
|
||||
, m_read_pos(0)
|
||||
, m_resolver(ios)
|
||||
, m_handler(handler)
|
||||
, m_connect_handler(ch)
|
||||
, m_filter_handler(fh)
|
||||
, m_timer(ios)
|
||||
, m_last_receive(time_now())
|
||||
, m_bottled(bottled)
|
||||
, m_called(false)
|
||||
, http_filter_handler const& fh = http_filter_handler()
|
||||
#ifdef TORRENT_USE_OPENSSL
|
||||
, m_ssl_ctx(ios, asio::ssl::context::sslv23_client)
|
||||
, boost::asio::ssl::context* ssl_ctx = 0
|
||||
#endif
|
||||
, m_rate_limit(0)
|
||||
, m_download_quota(0)
|
||||
, m_limiter_timer_active(false)
|
||||
, m_limiter_timer(ios)
|
||||
, m_redirects(5)
|
||||
, m_connection_ticket(-1)
|
||||
, m_cc(cc)
|
||||
, m_ssl(false)
|
||||
, m_priority(0)
|
||||
, m_abort(false)
|
||||
{
|
||||
TORRENT_ASSERT(!m_handler.empty());
|
||||
#ifdef TORRENT_USE_OPENSSL
|
||||
error_code ec;
|
||||
m_ssl_ctx.set_verify_mode(asio::ssl::context::verify_none, ec);
|
||||
#endif
|
||||
}
|
||||
);
|
||||
|
||||
~http_connection();
|
||||
|
||||
void rate_limit(int limit);
|
||||
|
||||
|
@ -191,7 +164,8 @@ private:
|
|||
|
||||
std::list<tcp::endpoint> m_endpoints;
|
||||
#ifdef TORRENT_USE_OPENSSL
|
||||
asio::ssl::context m_ssl_ctx;
|
||||
asio::ssl::context* m_ssl_ctx;
|
||||
bool m_own_ssl_context;
|
||||
#endif
|
||||
|
||||
// the current download limit, in bytes per second
|
||||
|
|
|
@ -921,6 +921,10 @@ namespace libtorrent
|
|||
// the object.
|
||||
piece_manager* m_storage;
|
||||
|
||||
#ifdef TORRENT_USE_OPENSSL
|
||||
boost::shared_ptr<asio::ssl::context> m_ssl_ctx;
|
||||
#endif
|
||||
|
||||
#ifdef TORRENT_DEBUG
|
||||
public:
|
||||
#endif
|
||||
|
|
|
@ -333,6 +333,8 @@ namespace libtorrent
|
|||
// ------- end deprecation -------
|
||||
#endif
|
||||
|
||||
std::string const& ssl_cert() const { return m_ssl_root_cert; }
|
||||
|
||||
bool is_valid() const { return m_files.is_valid(); }
|
||||
|
||||
bool priv() const { return m_private; }
|
||||
|
@ -456,6 +458,11 @@ namespace libtorrent
|
|||
// to create the torrent file
|
||||
std::string m_created_by;
|
||||
|
||||
// for ssl-torrens, this contains the root
|
||||
// certificate, in .pem format (i.e. ascii
|
||||
// base64 encoded with head and tails)
|
||||
std::string m_ssl_root_cert;
|
||||
|
||||
// the info section parsed. points into m_info_section
|
||||
// parsed lazily
|
||||
mutable lazy_entry m_info_dict;
|
||||
|
|
|
@ -63,6 +63,9 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
#include "libtorrent/intrusive_ptr_base.hpp"
|
||||
#include "libtorrent/size_type.hpp"
|
||||
#include "libtorrent/union_endpoint.hpp"
|
||||
#ifdef TORRENT_USE_OPENSSL
|
||||
#include <boost/asio/ssl/context.hpp>
|
||||
#endif
|
||||
|
||||
namespace libtorrent
|
||||
{
|
||||
|
@ -90,6 +93,9 @@ namespace libtorrent
|
|||
, num_want(0)
|
||||
, send_stats(true)
|
||||
, apply_ip_filter(true)
|
||||
#ifdef TORRENT_USE_OPENSSL
|
||||
, ssl_ctx(0)
|
||||
#endif
|
||||
{}
|
||||
|
||||
enum
|
||||
|
@ -125,6 +131,9 @@ namespace libtorrent
|
|||
address bind_ip;
|
||||
bool send_stats;
|
||||
bool apply_ip_filter;
|
||||
#ifdef TORRENT_USE_OPENSSL
|
||||
boost::asio::ssl::context* ssl_ctx;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct TORRENT_EXPORT request_callback
|
||||
|
|
|
@ -298,6 +298,9 @@ namespace libtorrent
|
|||
|
||||
info["name"] = m_files.name();
|
||||
|
||||
if (!m_root_cert.empty())
|
||||
info["ssl-cert"] = m_root_cert;
|
||||
|
||||
if (m_private) info["private"] = 1;
|
||||
|
||||
if (!m_multifile)
|
||||
|
@ -446,6 +449,11 @@ namespace libtorrent
|
|||
, boost::bind(&announce_entry::second, _1) < boost::bind(&announce_entry::second, _2));
|
||||
}
|
||||
|
||||
void create_torrent::set_root_cert(std::string const& cert)
|
||||
{
|
||||
m_root_cert = cert;
|
||||
}
|
||||
|
||||
void create_torrent::set_hash(int index, sha1_hash const& h)
|
||||
{
|
||||
TORRENT_ASSERT(index >= 0);
|
||||
|
|
|
@ -50,6 +50,52 @@ namespace libtorrent {
|
|||
|
||||
enum { max_bottled_buffer = 1024 * 1024 };
|
||||
|
||||
http_connection::http_connection(io_service& ios, connection_queue& cc
|
||||
, http_handler const& handler, bool bottled
|
||||
, http_connect_handler const& ch
|
||||
, http_filter_handler const& fh
|
||||
#ifdef TORRENT_USE_OPENSSL
|
||||
, boost::asio::ssl::context* ssl_ctx
|
||||
#endif
|
||||
)
|
||||
: m_sock(ios)
|
||||
#if TORRENT_USE_I2P
|
||||
, m_i2p_conn(0)
|
||||
#endif
|
||||
, m_read_pos(0)
|
||||
, m_resolver(ios)
|
||||
, m_handler(handler)
|
||||
, m_connect_handler(ch)
|
||||
, m_filter_handler(fh)
|
||||
, m_timer(ios)
|
||||
, m_last_receive(time_now())
|
||||
, m_bottled(bottled)
|
||||
, m_called(false)
|
||||
#ifdef TORRENT_USE_OPENSSL
|
||||
, m_ssl_ctx(ssl_ctx)
|
||||
, m_own_ssl_context(false)
|
||||
#endif
|
||||
, m_rate_limit(0)
|
||||
, m_download_quota(0)
|
||||
, m_limiter_timer_active(false)
|
||||
, m_limiter_timer(ios)
|
||||
, m_redirects(5)
|
||||
, m_connection_ticket(-1)
|
||||
, m_cc(cc)
|
||||
, m_ssl(false)
|
||||
, m_priority(0)
|
||||
, m_abort(false)
|
||||
{
|
||||
TORRENT_ASSERT(!m_handler.empty());
|
||||
}
|
||||
|
||||
http_connection::~http_connection()
|
||||
{
|
||||
#ifdef TORRENT_USE_OPENSSL
|
||||
if (m_own_ssl_context) delete m_ssl_ctx;
|
||||
#endif
|
||||
}
|
||||
|
||||
void http_connection::get(std::string const& url, time_duration timeout, int prio
|
||||
, proxy_settings const* ps, int handle_redirects, std::string const& user_agent
|
||||
, address const& bind_addr
|
||||
|
@ -250,7 +296,20 @@ void http_connection::start(std::string const& hostname, std::string const& port
|
|||
|
||||
void* userdata = 0;
|
||||
#ifdef TORRENT_USE_OPENSSL
|
||||
if (m_ssl) userdata = &m_ssl_ctx;
|
||||
if (m_ssl)
|
||||
{
|
||||
if (m_ssl_ctx == 0)
|
||||
{
|
||||
m_ssl_ctx = new boost::asio::ssl::context(m_resolver.get_io_service(), asio::ssl::context::sslv23_client);
|
||||
if (m_ssl_ctx)
|
||||
{
|
||||
m_own_ssl_context = true;
|
||||
error_code ec;
|
||||
m_ssl_ctx->set_verify_mode(asio::ssl::context::verify_none, ec);
|
||||
}
|
||||
}
|
||||
userdata = m_ssl_ctx;
|
||||
}
|
||||
#endif
|
||||
instantiate_connection(m_resolver.get_io_service()
|
||||
, proxy ? *proxy : null_proxy, m_sock, userdata);
|
||||
|
|
|
@ -216,7 +216,11 @@ namespace libtorrent
|
|||
, boost::bind(&http_tracker_connection::on_response, self(), _1, _2, _3, _4)
|
||||
, true
|
||||
, boost::bind(&http_tracker_connection::on_connect, self(), _1)
|
||||
, boost::bind(&http_tracker_connection::on_filter, self(), _1, _2)));
|
||||
, boost::bind(&http_tracker_connection::on_filter, self(), _1, _2)
|
||||
#ifdef TORRENT_USE_OPENSSL
|
||||
, tracker_req().ssl_ctx
|
||||
#endif
|
||||
));
|
||||
|
||||
int timeout = tracker_req().event==tracker_request::stopped
|
||||
?settings.stop_tracker_timeout
|
||||
|
|
108
src/torrent.cpp
108
src/torrent.cpp
|
@ -84,6 +84,8 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
|
||||
#ifdef TORRENT_USE_OPENSSL
|
||||
#include "libtorrent/ssl_stream.hpp"
|
||||
#include <boost/asio/ssl/context.hpp>
|
||||
#include <boost/asio/ssl/verify_context.hpp>
|
||||
#endif
|
||||
|
||||
#if TORRENT_USE_IOSTREAM
|
||||
|
@ -1261,6 +1263,15 @@ namespace libtorrent
|
|||
|
||||
#endif
|
||||
|
||||
/*
|
||||
#ifdef TORRENT_USE_OPENSSL
|
||||
bool verify_function(bool preverified, boost::asio::ssl::verify_context& ctx)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
*/
|
||||
|
||||
// this may not be called from a constructor because of the call to
|
||||
// shared_from_this()
|
||||
void torrent::init()
|
||||
|
@ -1270,6 +1281,98 @@ namespace libtorrent
|
|||
TORRENT_ASSERT(m_torrent_file->num_files() > 0);
|
||||
TORRENT_ASSERT(m_torrent_file->total_size() >= 0);
|
||||
|
||||
#ifdef TORRENT_USE_OPENSSL
|
||||
std::string cert = m_torrent_file->ssl_cert();
|
||||
if (!cert.empty())
|
||||
{
|
||||
using boost::asio::ssl::context;
|
||||
|
||||
// create the SSL context for this torrent. We need to
|
||||
// inject the root certificate, and no other, to
|
||||
// verify other peers against
|
||||
boost::shared_ptr<context> ctx(
|
||||
new (std::nothrow) context(m_ses.m_io_service, context::tlsv1));
|
||||
|
||||
if (!ctx)
|
||||
{
|
||||
set_error(asio::error::no_memory, "SSL context");
|
||||
pause();
|
||||
return;
|
||||
}
|
||||
|
||||
error_code ec;
|
||||
ctx->set_verify_mode(context::verify_peer
|
||||
| context::verify_fail_if_no_peer_cert, ec);
|
||||
if (ec)
|
||||
{
|
||||
set_error(ec, "SSL context");
|
||||
pause();
|
||||
return;
|
||||
}
|
||||
|
||||
// this is used for debugging
|
||||
/*
|
||||
ctx->set_verify_callback(verify_function, ec);
|
||||
if (ec)
|
||||
{
|
||||
set_error(ec, "SSL verify callback");
|
||||
pause();
|
||||
return;
|
||||
}
|
||||
*/
|
||||
SSL_CTX* ssl_ctx = ctx->impl();
|
||||
|
||||
// we don't want regular peers to be able to invite others
|
||||
// by in turn signing new certificates. So, break the verification
|
||||
// chain at depth 2. This is just a precaution in case the
|
||||
// issuer of the peer certificates made a mistake and issued them
|
||||
// as CA certs.
|
||||
SSL_CTX_set_verify_depth(ssl_ctx, 0);
|
||||
|
||||
// create a new x.509 certificate store
|
||||
X509_STORE* cert_store = X509_STORE_new();
|
||||
if (!cert_store)
|
||||
{
|
||||
set_error(asio::error::no_memory, "x.509 certificate store");
|
||||
pause();
|
||||
return;
|
||||
}
|
||||
|
||||
// wrap the PEM certificate in a BIO, for openssl to read
|
||||
BIO* bp = BIO_new_mem_buf((void*)cert.c_str(), cert.size());
|
||||
|
||||
// parse the certificate into OpenSSL's internal
|
||||
// representation
|
||||
X509* certificate = PEM_read_bio_X509_AUX(bp, 0, 0, 0);
|
||||
|
||||
BIO_free(bp);
|
||||
|
||||
if (!certificate)
|
||||
{
|
||||
X509_STORE_free(cert_store);
|
||||
set_error(asio::error::no_memory, "x.509 certificate");
|
||||
pause();
|
||||
return;
|
||||
}
|
||||
|
||||
// add cert to cert_store
|
||||
X509_STORE_add_cert(cert_store, certificate);
|
||||
|
||||
// and lastly, replace the default cert store with ours
|
||||
SSL_CTX_set_cert_store(ssl_ctx, cert_store);
|
||||
#if 0
|
||||
char filename[100];
|
||||
snprintf(filename, sizeof(filename), "/tmp/%d.pem", rand());
|
||||
FILE* f = fopen(filename, "w+");
|
||||
fwrite(cert.c_str(), cert.size(), 1, f);
|
||||
fclose(f);
|
||||
ctx->load_verify_file(filename);
|
||||
#endif
|
||||
// if all went well, set the torrent ssl context to this one
|
||||
m_ssl_ctx = ctx;
|
||||
}
|
||||
#endif
|
||||
|
||||
m_file_priority.resize(m_torrent_file->num_files(), 1);
|
||||
m_file_progress.resize(m_torrent_file->num_files(), 0);
|
||||
|
||||
|
@ -1941,6 +2044,11 @@ namespace libtorrent
|
|||
req.corrupt = m_total_failed_bytes;
|
||||
req.left = bytes_left();
|
||||
if (req.left == -1) req.left = 16*1024;
|
||||
#ifdef TORRENT_USE_OPENSSL
|
||||
// if this torrent contains an SSL certificate, make sure
|
||||
// any SSL tracker presents a certificate signed by it
|
||||
req.ssl_ctx = m_ssl_ctx.get();
|
||||
#endif
|
||||
|
||||
// exclude redundant bytes if we should
|
||||
if (!settings().report_true_downloaded)
|
||||
|
|
|
@ -932,6 +932,9 @@ namespace libtorrent
|
|||
}
|
||||
|
||||
m_private = info.dict_find_int_value("private", 0);
|
||||
|
||||
m_ssl_root_cert = info.dict_find_string_value("ssl-cert");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue