Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

http: add client identity #445

Merged
merged 4 commits into from
Oct 15, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion include/opendht/dht_proxy_client.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ class OPENDHT_PUBLIC DhtProxyClient final : public DhtInterface {
DhtProxyClient();

explicit DhtProxyClient(
std::shared_ptr<dht::crypto::Certificate> certificate,
std::shared_ptr<dht::crypto::Certificate> serverCA, dht::crypto::Identity clientIdentity,
std::function<void()> loopSignal, const std::string& serverHost,
const std::string& pushClientId = "", std::shared_ptr<dht::Logger> logger = {});

Expand Down Expand Up @@ -327,6 +327,7 @@ class OPENDHT_PUBLIC DhtProxyClient final : public DhtInterface {
*/
void cancelAllOperations();

dht::crypto::Identity clientIdentity_;
std::shared_ptr<dht::crypto::Certificate> serverCertificate_;
std::pair<std::string, std::string> serverHostService_;
std::string pushClientId_;
Expand Down
2 changes: 0 additions & 2 deletions include/opendht/dht_proxy_server.h
Original file line number Diff line number Diff line change
Expand Up @@ -322,8 +322,6 @@ class OPENDHT_PUBLIC DhtProxyServer
std::thread serverThread_;
std::unique_ptr<restinio::http_server_t<RestRouterTraitsTls>> httpsServer_;
std::unique_ptr<restinio::http_server_t<RestRouterTraits>> httpServer_;
std::unique_ptr<asio::const_buffer> pk_;
std::unique_ptr<asio::const_buffer> cc_;

// http client
std::pair<std::string, std::string> pushHostPort_;
Expand Down
3 changes: 2 additions & 1 deletion include/opendht/dhtrunner.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ class OPENDHT_PUBLIC DhtRunner {
std::string push_token {};
bool peer_discovery {false};
bool peer_publish {false};
std::shared_ptr<dht::crypto::Certificate> client_cert;
std::shared_ptr<dht::crypto::Certificate> server_ca;
dht::crypto::Identity client_identity;
};

struct Context {
Expand Down
16 changes: 8 additions & 8 deletions include/opendht/http.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

#include "def.h"
#include "infohash.h"
#include "crypto.h"

// some libraries may try to redefine snprintf
// but restinio will use it in std namespace
Expand Down Expand Up @@ -85,17 +86,15 @@ class OPENDHT_PUBLIC Connection
{
public:
Connection(asio::io_context& ctx, const bool ssl = true, std::shared_ptr<dht::Logger> l = {});
Connection(asio::io_context& ctx, std::shared_ptr<dht::crypto::Certificate> certificate,
std::shared_ptr<dht::Logger> l = {});
Connection(asio::io_context& ctx, std::shared_ptr<dht::crypto::Certificate> server_ca,
const dht::crypto::Identity& identity, std::shared_ptr<dht::Logger> l = {});
~Connection();

unsigned int id();
bool is_open();
bool is_v6();
bool is_ssl();

void set_endpoint(const asio::ip::tcp::endpoint& endpoint,
const asio::ssl::verify_mode verify_mode = asio::ssl::verify_none);
void set_ssl_verification(const asio::ip::tcp::endpoint& endpoint, const asio::ssl::verify_mode verify_mode);

asio::streambuf& input();
asio::streambuf& data();
Expand All @@ -120,7 +119,6 @@ class OPENDHT_PUBLIC Connection
std::unique_ptr<socket_t> socket_;
std::shared_ptr<asio::ssl::context> ssl_ctx_;
std::unique_ptr<ssl_socket_t> ssl_socket_;
std::unique_ptr<asio::const_buffer> certificate_;

asio::ip::tcp::endpoint endpoint_;

Expand Down Expand Up @@ -232,7 +230,8 @@ class OPENDHT_PUBLIC Request
return request_;
}

void set_certificate(std::shared_ptr<dht::crypto::Certificate> certificate);
void set_certificate_authority(std::shared_ptr<dht::crypto::Certificate> certificate);
void set_identity(const dht::crypto::Identity& identity);
void set_logger(std::shared_ptr<dht::Logger> logger);

/**
Expand Down Expand Up @@ -311,7 +310,8 @@ class OPENDHT_PUBLIC Request
std::unique_ptr<Callbacks> cbs_;
State state_;

std::shared_ptr<dht::crypto::Certificate> certificate_;
dht::crypto::Identity client_identity_;
std::shared_ptr<dht::crypto::Certificate> server_ca_;
std::string service_;
std::string host_;

Expand Down
37 changes: 25 additions & 12 deletions src/dht_proxy_client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,20 +72,25 @@ struct DhtProxyClient::ProxySearch {
DhtProxyClient::DhtProxyClient() {}

DhtProxyClient::DhtProxyClient(
std::shared_ptr<dht::crypto::Certificate> certificate,
std::shared_ptr<dht::crypto::Certificate> serverCA, dht::crypto::Identity clientIdentity,
std::function<void()> signal, const std::string& serverHost,
const std::string& pushClientId, std::shared_ptr<dht::Logger> logger)
:
serverCertificate_(certificate),
clientIdentity_(clientIdentity), serverCertificate_(serverCA),
pushClientId_(pushClientId), loopSignal_(signal), logger_(logger)
{
// build http client
serverHostService_ = splitPort(serverHost);
serverHostService_.second = serverHostService_.second.empty() ? "80" :
serverHostService_.second;
if (serverCertificate_ and logger_)
logger_->d("[proxy:client] using server certificate for ssl:\n%s",
serverCertificate_->toString(false/*chain*/).c_str());
if (logger_){
if (serverCertificate_)
logger_->d("[proxy:client] using ca certificate for ssl:\n%s",
serverCertificate_->toString(false/*chain*/).c_str());
if (clientIdentity_.first and clientIdentity_.second)
logger_->d("[proxy:client] using client certificate for ssl:\n%s",
clientIdentity_.second->toString(false/*chain*/).c_str());
}
// resolve once
resolver_ = std::make_shared<http::Resolver>(httpContext_, serverHost, logger_);
// run http client
Expand Down Expand Up @@ -346,8 +351,9 @@ DhtProxyClient::get(const InfoHash& key, GetCallback cb, DoneCallback donecb, Va
}
});
if (serverCertificate_)
request->set_certificate(serverCertificate_);
request->set_certificate(serverCertificate_);
request->set_certificate_authority(serverCertificate_);
if (clientIdentity_.first and clientIdentity_.second)
request->set_identity(clientIdentity_);
request->send();
requests_[reqid] = request;
}
Expand Down Expand Up @@ -482,8 +488,9 @@ DhtProxyClient::doPut(const InfoHash& key, Sp<Value> val, DoneCallback cb, time_
}
});
if (serverCertificate_)
request->set_certificate(serverCertificate_);
request->set_certificate(serverCertificate_);
request->set_certificate_authority(serverCertificate_);
if (clientIdentity_.first and clientIdentity_.second)
request->set_identity(clientIdentity_);
request->send();
requests_[reqid] = request;
}
Expand Down Expand Up @@ -679,7 +686,9 @@ DhtProxyClient::queryProxyInfo(std::shared_ptr<InfoState> infoState, const sa_fa
return;

if (serverCertificate_)
request->set_certificate(serverCertificate_);
request->set_certificate_authority(serverCertificate_);
if (clientIdentity_.first and clientIdentity_.second)
request->set_identity(clientIdentity_);
request->send();
requests_[reqid] = request;
}
Expand Down Expand Up @@ -958,7 +967,9 @@ DhtProxyClient::handleExpireListener(const asio::error_code &ec, const InfoHash&
}
});
if (serverCertificate_)
request->set_certificate(serverCertificate_);
request->set_certificate_authority(serverCertificate_);
if (clientIdentity_.first and clientIdentity_.second)
request->set_identity(clientIdentity_);
request->send();
requests_[reqid] = request;
}
Expand Down Expand Up @@ -1065,7 +1076,9 @@ DhtProxyClient::sendListen(const restinio::http_request_header_t header,
}
});
if (serverCertificate_)
request->set_certificate(serverCertificate_);
request->set_certificate_authority(serverCertificate_);
if (clientIdentity_.first and clientIdentity_.second)
request->set_identity(clientIdentity_);
request->send();
requests_[reqid] = request;
}
Expand Down
13 changes: 6 additions & 7 deletions src/dht_proxy_server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -246,19 +246,18 @@ DhtProxyServer::DhtProxyServer(
SSL_CTX_set_options(tls_context.native_handle(), SSL_OP_NO_RENEGOTIATION); // CVE-2009-3555
#endif
// node private key
auto pk = identity.first->serialize();
pk_ = std::make_unique<asio::const_buffer>(static_cast<void*>(pk.data()), (std::size_t) pk.size());
tls_context.use_private_key(*pk_, asio::ssl::context::file_format::pem, ec);
auto key = identity.first->serialize();
tls_context.use_private_key(asio::const_buffer{key.data(), key.size()},
asio::ssl::context::file_format::pem, ec);
if (ec)
throw std::runtime_error("Error setting node's private key: " + ec.message());
// certificate chain
auto cc = identity.second->toString(true/*chain*/);
cc_ = std::make_unique<asio::const_buffer>(static_cast<const void*>(cc.data()), (std::size_t) cc.size());
tls_context.use_certificate_chain(*cc_, ec);
auto certchain = identity.second->toString(true/*chain*/);
tls_context.use_certificate_chain(asio::const_buffer{certchain.data(), certchain.size()}, ec);
if (ec)
throw std::runtime_error("Error setting certificate chain: " + ec.message());
if (logger_)
logger_->d("[proxy:server] using certificate chain for ssl:\n%s", cc.c_str());
logger_->d("[proxy:server] using certificate chain for ssl:\n%s", certchain.c_str());
// build http server
auto settings = restinio::run_on_this_thread_settings_t<RestRouterTraitsTls>();
addServerSettings(settings);
Expand Down
3 changes: 2 additions & 1 deletion src/dhtrunner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -979,7 +979,8 @@ DhtRunner::enableProxy(bool proxify)
// Init the proxy client
auto dht_via_proxy = std::unique_ptr<DhtInterface>(
new DhtProxyClient(
config_.client_cert,
config_.server_ca,
config_.client_identity,
[this]{
if (config_.threaded) {
{
Expand Down
78 changes: 45 additions & 33 deletions src/http.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,23 +99,36 @@ Connection::Connection(asio::io_context& ctx, const bool ssl, std::shared_ptr<dh
}
}

Connection::Connection(asio::io_context& ctx, std::shared_ptr<dht::crypto::Certificate> certificate,
std::shared_ptr<dht::Logger> l)
Connection::Connection(asio::io_context& ctx, std::shared_ptr<dht::crypto::Certificate> server_ca,
const dht::crypto::Identity& identity, std::shared_ptr<dht::Logger> l)
: id_(Connection::ids_++), ctx_(ctx), logger_(l)
{
ssl_ctx_ = std::make_shared<asio::ssl::context>(asio::ssl::context::sslv23);
ssl_ctx_->set_default_verify_paths();

asio::error_code ec;
auto cert = certificate->toString(false/*chain*/);
certificate_ = std::make_unique<asio::const_buffer>(static_cast<const void*>(cert.data()),
(std::size_t) cert.size());
ssl_ctx_->use_certificate(*certificate_, asio::ssl::context::file_format::pem, ec);
if (ec)
throw std::runtime_error("Error setting certificate: " + ec.message());
else if (logger_)
logger_->d("[http:client] [connection:%i] start https session with %s", id_, certificate->getUID().c_str());

if (server_ca){
auto ca = server_ca->toString(false/*chain*/);
ssl_ctx_->add_certificate_authority(asio::const_buffer{ca.data(), ca.size()}, ec);
if (ec)
throw std::runtime_error("Error adding certificate authority: " + ec.message());
else if (logger_)
logger_->d("[http:client] [connection:%i] certficate authority %s", id_, server_ca->getUID().c_str());
}
if (identity.first){
auto key = identity.first->serialize();
ssl_ctx_->use_private_key(asio::const_buffer{key.data(), key.size()},
asio::ssl::context::file_format::pem, ec);
if (ec)
throw std::runtime_error("Error setting client private key: " + ec.message());
}
if (identity.second){
auto cert = identity.second->toString(true/*chain*/);
ssl_ctx_->use_certificate_chain(asio::const_buffer{cert.data(), cert.size()}, ec);
if (ec)
throw std::runtime_error("Error adding client certificate: " + ec.message());
else if (logger_)
logger_->d("[http:client] [connection:%i] client certificate %s", id_, identity.second->getUID().c_str());
}
ssl_ctx_->set_verify_mode(asio::ssl::verify_peer | asio::ssl::verify_fail_if_no_peer_cert);
ssl_socket_ = std::make_unique<ssl_socket_t>(ctx_, ssl_ctx_);
}
Expand Down Expand Up @@ -154,33 +167,28 @@ Connection::is_open()
return socket_->is_open();
}

bool
Connection::is_v6()
{
return endpoint_.address().is_v6();
}

bool
Connection::is_ssl()
{
return ssl_ctx_ ? true : false;
}

void
Connection::set_endpoint(const asio::ip::tcp::endpoint& endpoint, const asio::ssl::verify_mode verify_mode)
Connection::set_ssl_verification(const asio::ip::tcp::endpoint& endpoint, const asio::ssl::verify_mode verify_mode)
{
endpoint_ = endpoint;
if (ssl_ctx_ and verify_mode != asio::ssl::verify_none){
auto hostname = endpoint_.address().to_string();
auto hostname = endpoint.address().to_string();
ssl_socket_->asio_ssl_stream().set_verify_mode(verify_mode);
ssl_socket_->asio_ssl_stream().set_verify_callback(
[this, hostname](bool preverified, asio::ssl::verify_context& ctx) -> bool {
if (preverified)
return preverified;
// starts from CA and goes down the presented chain
auto verifier = asio::ssl::rfc2818_verification(hostname);
bool verified = verifier(preverified, ctx);
auto verify_ec = X509_STORE_CTX_get_error(ctx.native_handle());
if (verify_ec == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT /*18*/
|| verify_ec == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN /*19*/)
verified = true;
if (verified != 0 /*X509_V_OK*/ and logger_)
logger_->e("[http::connection:%i] ssl verification error=%i", id_, verify_ec);
return verified;
}
);
Expand Down Expand Up @@ -494,9 +502,15 @@ Request::get_connection() const
}

void
Request::set_certificate(std::shared_ptr<dht::crypto::Certificate> certificate)
Request::set_certificate_authority(std::shared_ptr<dht::crypto::Certificate> certificate)
{
server_ca_ = certificate;
}

void
Request::set_identity(const dht::crypto::Identity& identity)
{
certificate_ = certificate;
client_identity_ = identity;
}

void
Expand Down Expand Up @@ -728,8 +742,8 @@ Request::connect(std::vector<asio::ip::tcp::endpoint>&& endpoints, HandlerCb cb)
logger_->d("[http:client] [request:%i] connect begin: %s", id_, eps.c_str());
}
if (get_url().protocol == "https"){
if (certificate_)
conn_ = std::make_shared<Connection>(ctx_, certificate_, logger_);
if (server_ca_)
conn_ = std::make_shared<Connection>(ctx_, server_ca_, client_identity_, logger_);
else
conn_ = std::make_shared<Connection>(ctx_, true/*ssl*/, logger_);
}
Expand All @@ -750,9 +764,9 @@ Request::connect(std::vector<asio::ip::tcp::endpoint>&& endpoints, HandlerCb cb)
logger_->d("[http:client] [request:%i] connect success", id_);

if (get_url().protocol == "https"){
if (certificate_)
conn_->set_endpoint(endpoint, asio::ssl::verify_peer
| asio::ssl::verify_fail_if_no_peer_cert);
if (server_ca_)
conn_->set_ssl_verification(endpoint, asio::ssl::verify_peer
| asio::ssl::verify_fail_if_no_peer_cert);

if (conn_ and conn_->is_open() and conn_->is_ssl()){
conn_->async_handshake([this, cb](const asio::error_code& ec){
Expand All @@ -770,8 +784,6 @@ Request::connect(std::vector<asio::ip::tcp::endpoint>&& endpoints, HandlerCb cb)
cb(asio::error::operation_aborted);
return;
}
else
conn_->set_endpoint(endpoint, asio::ssl::verify_none);
}
if (cb)
cb(ec);
Expand Down
7 changes: 4 additions & 3 deletions tests/dhtproxytester.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,12 @@ DhtProxyTester::setUp() {

serverProxy = std::unique_ptr<dht::DhtProxyServer>(
new dht::DhtProxyServer(
///*http*/dht::crypto::Identity{},
/*https*/serverIdentity,
//dht::crypto::Identity{}, // http
serverIdentity, // https
nodeProxy, 8080, /*pushServer*/"127.0.0.1:8090", logger));

clientConfig.client_cert = serverIdentity.second;
clientConfig.server_ca = serverCAIdentity.second;
clientConfig.client_identity = dht::crypto::generateIdentity("DhtProxyTester");
clientConfig.dht_config.node_config.maintain_storage = false;
clientConfig.threaded = true;
clientConfig.push_node_id = "dhtnode";
Expand Down