initial attempt at verifying the certificate of ssl trackers, by including the certificate in the .torrent file

This commit is contained in:
Arvid Norberg 2011-08-28 21:06:15 +00:00
parent e7185b519b
commit a286a6b4aa
11 changed files with 236 additions and 35 deletions

View File

@ -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());

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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)

View File

@ -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;
}