From ccfb76d9b478774669b10cb71148c02333bc4f7c Mon Sep 17 00:00:00 2001 From: Seva <seva@binarytrails.net> Date: Wed, 25 Sep 2019 15:38:54 -0400 Subject: [PATCH] http: add client identity & fix CA --- include/opendht/dht_proxy_client.h | 3 +- include/opendht/dhtrunner.h | 3 +- include/opendht/http.h | 19 ++++--- src/dht_proxy_client.cpp | 37 +++++++++----- src/dhtrunner.cpp | 3 +- src/http.cpp | 82 ++++++++++++++++++------------ tests/dhtproxytester.cpp | 7 +-- 7 files changed, 95 insertions(+), 59 deletions(-) diff --git a/include/opendht/dht_proxy_client.h b/include/opendht/dht_proxy_client.h index 40918735..4f84cc7b 100644 --- a/include/opendht/dht_proxy_client.h +++ b/include/opendht/dht_proxy_client.h @@ -53,7 +53,7 @@ public: 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 = {}); @@ -327,6 +327,7 @@ private: */ void cancelAllOperations(); + dht::crypto::Identity clientIdentity_; std::shared_ptr<dht::crypto::Certificate> serverCertificate_; std::pair<std::string, std::string> serverHostService_; std::string pushClientId_; diff --git a/include/opendht/dhtrunner.h b/include/opendht/dhtrunner.h index 2e2590bb..1b725d71 100644 --- a/include/opendht/dhtrunner.h +++ b/include/opendht/dhtrunner.h @@ -63,7 +63,8 @@ public: 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 { diff --git a/include/opendht/http.h b/include/opendht/http.h index 72476827..36656898 100644 --- a/include/opendht/http.h +++ b/include/opendht/http.h @@ -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 @@ -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(); @@ -120,7 +119,9 @@ private: 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_; + std::unique_ptr<asio::const_buffer> server_ca_; + std::unique_ptr<asio::const_buffer> client_key_; + std::unique_ptr<asio::const_buffer> client_cert_; asio::ip::tcp::endpoint endpoint_; @@ -232,7 +233,8 @@ public: 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); /** @@ -311,7 +313,8 @@ private: 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_; diff --git a/src/dht_proxy_client.cpp b/src/dht_proxy_client.cpp index 94e793e7..e7ffad3d 100644 --- a/src/dht_proxy_client.cpp +++ b/src/dht_proxy_client.cpp @@ -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 @@ -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; } @@ -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; } @@ -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; } @@ -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; } @@ -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; } diff --git a/src/dhtrunner.cpp b/src/dhtrunner.cpp index 1187e50b..98edb344 100644 --- a/src/dhtrunner.cpp +++ b/src/dhtrunner.cpp @@ -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) { { diff --git a/src/http.cpp b/src/http.cpp index 9b60c890..f52b459a 100644 --- a/src/http.cpp +++ b/src/http.cpp @@ -97,23 +97,40 @@ 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*/); + server_ca_ = std::make_unique<asio::const_buffer>(static_cast<const void*>(ca.data()), + (std::size_t) ca.size()); + ssl_ctx_->add_certificate_authority(*server_ca_, 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 pk = identity.first->serialize(); + client_key_ = std::make_unique<asio::const_buffer>(static_cast<void*>(pk.data()), (std::size_t) pk.size()); + ssl_ctx_->use_private_key(*client_key_, asio::ssl::context::file_format::pem, ec); + if (ec) + throw std::runtime_error("Error setting client private key: " + ec.message()); + } + if (identity.second){ + auto c = identity.second->toString(false/*chain*/); + client_cert_ = std::make_unique<asio::const_buffer>(static_cast<const void*>(c.data()), + (std::size_t) c.size()); + ssl_ctx_->use_certificate(*client_cert_, asio::ssl::context::file_format::pem, 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_); } @@ -152,12 +169,6 @@ Connection::is_open() return socket_->is_open(); } -bool -Connection::is_v6() -{ - return endpoint_.address().is_v6(); -} - bool Connection::is_ssl() { @@ -165,20 +176,21 @@ Connection::is_ssl() } 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; } ); @@ -492,9 +504,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 @@ -726,8 +744,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_); } @@ -748,9 +766,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){ @@ -768,8 +786,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); diff --git a/tests/dhtproxytester.cpp b/tests/dhtproxytester.cpp index e118ce88..41241f34 100644 --- a/tests/dhtproxytester.cpp +++ b/tests/dhtproxytester.cpp @@ -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"; -- GitLab