From 72317c07c37e9ad6f92148481a968bae1d3d7883 Mon Sep 17 00:00:00 2001 From: Jason Volk Date: Thu, 4 Jan 2018 23:14:21 -0800 Subject: [PATCH] ircd::net: Shlock certificate common-name verification into the net stack. --- include/ircd/net/net.h | 2 +- include/ircd/net/remote.h | 5 +- include/ircd/net/socket.h | 16 ++--- ircd/net.cc | 126 ++++++++++++++++++++++++++++++++++---- 4 files changed, 128 insertions(+), 21 deletions(-) diff --git a/include/ircd/net/net.h b/include/ircd/net/net.h index 44fb3ca45..54fd25000 100644 --- a/include/ircd/net/net.h +++ b/include/ircd/net/net.h @@ -83,7 +83,7 @@ namespace ircd::net std::shared_ptr connect(const remote &, const milliseconds &timeout = 30000ms); bool disconnect(socket &, const dc &type = dc::SSL_NOTIFY) noexcept; - ctx::future> open(const ipport &, const milliseconds &timeout = 30000ms); + ctx::future> open(const ipport &, std::string cn, const milliseconds &timeout = 30000ms); ctx::future> open(const hostport &, const milliseconds &timeout = 30000ms); } diff --git a/include/ircd/net/remote.h b/include/ircd/net/remote.h index 872d422a2..507d5aaa9 100644 --- a/include/ircd/net/remote.h +++ b/include/ircd/net/remote.h @@ -129,8 +129,9 @@ struct ircd::net::ipport /// This structure combines features of hostport and ipport to hold a remote's /// resolved IP in bytes, a port number, and an optional hostname string which /// may have been used to resolve the IP, or may have been resolved from the -/// IP, or may just be empty, but either way still has some use being carried -/// along as part of this struct. +/// IP, or may be used for certificate Common Name verification, or may just +/// be empty, but anyway still has some use in most cases being carried along. +/// struct ircd::net::remote :ircd::net::ipport { diff --git a/include/ircd/net/socket.h b/include/ircd/net/socket.h index 70371d083..4acf12c1e 100644 --- a/include/ircd/net/socket.h +++ b/include/ircd/net/socket.h @@ -33,9 +33,10 @@ namespace ircd::net { struct socket; - extern asio::ssl::context sslv23_client; - std::shared_ptr connect(const ip::tcp::endpoint &remote, const milliseconds &timeout); + + bool handle_verify(bool, asio::ssl::verify_context &) noexcept; + extern asio::ssl::context sslv23_client; } struct ircd::net::socket @@ -66,9 +67,10 @@ struct ircd::net::socket bool timedout {false}; void call_user(const handler &, const error_code &ec) noexcept; + bool handle_verify(bool, asio::ssl::verify_context &, std::string cn) noexcept; void handle_timeout(std::weak_ptr wp, const error_code &ec) noexcept; void handle_handshake(std::weak_ptr wp, handler, const error_code &ec) noexcept; - void handle_connect(std::weak_ptr wp, handler, const error_code &ec) noexcept; + void handle_connect(std::weak_ptr wp, std::string cn, handler, const error_code &ec) noexcept; void handle(std::weak_ptr, handler, const error_code &) noexcept; public: @@ -120,16 +122,16 @@ struct ircd::net::socket bool cancel() noexcept; // SSL handshake after connect (untimed) - void handshake(const handshake_type &, handler callback); - void handshake(const handshake_type & = handshake_type::client); + void handshake(const handshake_type &, std::string cn, handler callback); + void handshake(const handshake_type &, std::string cn); // Connect to host (untimed) void connect(const endpoint &ep, handler callback); void connect(const endpoint &ep); // Connect to host and handshake composit (timed) - void open(const endpoint &ep, const milliseconds &timeout, handler callback); - void open(const endpoint &ep, const milliseconds &timeout); + void open(const endpoint &ep, std::string cn, const milliseconds &timeout, handler callback); + void open(const endpoint &ep, std::string cn, const milliseconds &timeout); bool disconnect(const dc &type); diff --git a/ircd/net.cc b/ircd/net.cc index 2599e1989..81db0c8aa 100644 --- a/ircd/net.cc +++ b/ircd/net.cc @@ -49,6 +49,9 @@ ircd::net::init::init() { assert(ircd::ios); resolve::resolver.reset(new ip::tcp::resolver{*ircd::ios}); + sslv23_client.set_verify_callback(net::handle_verify); + sslv23_client.set_verify_mode(asio::ssl::verify_peer); + sslv23_client.set_default_verify_paths(); } /// Network subsystem shutdown @@ -86,7 +89,7 @@ ircd::net::open(const hostport &hostport, { ctx::promise> p; ctx::future> f(p); - resolve(hostport, [p(std::move(p)), timeout] + resolve(hostport, [p(std::move(p)), timeout, cn(std::string(hostport.host))] (auto eptr, const ipport &ipport) mutable { @@ -95,7 +98,7 @@ ircd::net::open(const hostport &hostport, const auto ep{make_endpoint(ipport)}; const auto s(std::make_shared()); - s->open(ep, timeout, [p(std::move(p)), s] + s->open(ep, std::move(cn), timeout, [p(std::move(p)), s] (const error_code &ec) mutable { @@ -113,13 +116,14 @@ ircd::net::open(const hostport &hostport, ircd::ctx::future> ircd::net::open(const ipport &ipport, + std::string cn, const milliseconds &timeout) { ctx::promise> p; ctx::future> f(p); const auto ep{make_endpoint(ipport)}; const auto s(std::make_shared()); - s->open(ep, timeout, [p(std::move(p)), s] + s->open(ep, std::move(cn), timeout, [p(std::move(p)), s] (const error_code &ec) mutable { @@ -147,7 +151,7 @@ ircd::net::connect(const ip::tcp::endpoint &remote, const milliseconds &timeout) { const auto ret(std::make_shared()); - ret->open(remote, timeout); + ret->open(remote, std::string{}, timeout); return ret; } @@ -903,6 +907,7 @@ catch(const std::exception &e) void ircd::net::socket::open(const ip::tcp::endpoint &ep, + std::string cn, const milliseconds &timeout) try { @@ -914,12 +919,13 @@ try timeout.count()); connect(ep); - log.debug("socket(%p) connected to remote: %s from local: %s; performing handshake...", + log.debug("socket(%p) connected to remote: %s from local: %s; performing handshake to '%s'...", this, string(ep), - string(local())); + string(local()), + cn); - handshake(handshake_type::client); + handshake(handshake_type::client, std::move(cn)); log.debug("socket(%p) secure session with %s from local: %s established.", this, string(ep), @@ -940,17 +946,19 @@ catch(const std::exception &e) /// void ircd::net::socket::open(const ip::tcp::endpoint &ep, + std::string cn, const milliseconds &timeout, handler callback) { - log.debug("socket(%p) attempting connect to remote: %s for the next %ld$ms", + log.debug("socket(%p) attempting connect to remote: %s '%s' for the next %ld$ms", this, string(ep), + cn, timeout.count()); auto handler { - std::bind(&socket::handle_connect, this, weak_from(*this), std::move(callback), ph::_1) + std::bind(&socket::handle_connect, this, weak_from(*this), std::move(cn), std::move(callback), ph::_1) }; this->connect(ep, std::move(handler)); @@ -971,15 +979,29 @@ ircd::net::socket::connect(const ip::tcp::endpoint &ep, } void -ircd::net::socket::handshake(const handshake_type &type) +ircd::net::socket::handshake(const handshake_type &type, + std::string cn) { + auto handler + { + std::bind(&socket::handle_verify, this, ph::_1, ph::_2, std::move(cn)) + }; + + ssl.set_verify_callback(std::move(handler)); ssl.async_handshake(type, yield_context{to_asio{}}); } void ircd::net::socket::handshake(const handshake_type &type, + std::string cn, handler callback) { + auto handler + { + std::bind(&socket::handle_verify, this, ph::_1, ph::_2, std::move(cn)) + }; + + ssl.set_verify_callback(std::move(handler)); ssl.async_handshake(type, std::move(callback)); } @@ -1206,6 +1228,7 @@ catch(const std::exception &e) void ircd::net::socket::handle_connect(std::weak_ptr wp, + std::string cn, handler callback, const error_code &ec) noexcept try @@ -1231,7 +1254,7 @@ noexcept try std::bind(&socket::handle_handshake, this, wp, std::move(callback), ph::_1) }; - handshake(handshake_type::client, std::move(handler)); + handshake(handshake_type::client, std::move(cn), std::move(handler)); } catch(const std::bad_weak_ptr &e) { @@ -1328,6 +1351,66 @@ catch(const std::exception &e) e.what()); } +bool +ircd::net::socket::handle_verify(const bool valid, + asio::ssl::verify_context &vc, + std::string cn) +noexcept try +{ + assert(vc.native_handle()); + const auto &stctx{*vc.native_handle()}; + + bool verify; + switch(openssl::get_error(stctx)) + { + case X509_V_OK: + assert(valid); + verify = true; + break; + + case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: + assert(openssl::get_error_depth(stctx) == 0); + verify = true; + break; + + case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN: + verify = true; + break; + + default: + verify = false; + break; + } + + log.debug("handle_verify: verify_context(%p) %s %s :%s", + (const void *)&vc, + valid? "VALID" : "INVALID", + verify? "VERIFIABLE" : "UNVERIFIABLE", + openssl::get_error_string(stctx)); + + bool verified; + if(verify) + { + boost::asio::ssl::rfc2818_verification verify_cn{cn}; + verified = verify_cn(verify, vc); + } + else verified = false; + + if(!verified) + log.error("Failed to verify peer '%s' certificate: %s %s %s", + cn, + valid? "VALID" : "INVALID", + verify? "VERIFIABLE" : "UNVERIFIABLE", + openssl::get_error_string(stctx)); + + return verified; +} +catch(const std::exception &e) +{ + log.critical("socket::handle_verify: %s", e.what()); + return false; +} + void ircd::net::socket::call_user(const handler &callback, const error_code &ec) @@ -1562,6 +1645,27 @@ const return *ssl.native_handle(); } +bool +ircd::net::handle_verify(const bool preverified, + asio::ssl::verify_context &vc) +noexcept try +{ + assert(vc.native_handle()); + const auto &stctx{*vc.native_handle()}; + const auto &cert{openssl::current_cert(stctx)}; + log.debug("net::handle_verify: verify_context(%p): pre:%d :%s", + (const void *)&vc, + preverified, + openssl::get_error_string(stctx)); + + return preverified; +} +catch(const std::exception &e) +{ + log.critical("net::handle_verify: %s", e.what()); + return false; +} + /////////////////////////////////////////////////////////////////////////////// // // net/resolve.h