Perform RFC 2818 hostname verification in client SSL handshakes
If the verification mode is `system_ca`, clients will now do hostname verification. Thus, only certificates from expected hostnames are allowed when SSL is enabled. This can be overridden by forcible setting the SSL mode to autodetect. Clients will also send the hostname even when `system_ca` is not being performed. This leaks possible metadata, but allows servers providing multiple hostnames to respond with the correct certificate. One example is cloudflare, which getmonero.org is currently using.
This commit is contained in:
parent
0416764cae
commit
eca0fea45a
|
@ -174,7 +174,7 @@ namespace net_utils
|
||||||
// SSL Options
|
// SSL Options
|
||||||
if (ssl_support == epee::net_utils::ssl_support_t::e_ssl_support_enabled || ssl_support == epee::net_utils::ssl_support_t::e_ssl_support_autodetect)
|
if (ssl_support == epee::net_utils::ssl_support_t::e_ssl_support_enabled || ssl_support == epee::net_utils::ssl_support_t::e_ssl_support_autodetect)
|
||||||
{
|
{
|
||||||
if (!m_ssl_options.handshake(*m_ssl_socket, boost::asio::ssl::stream_base::client))
|
if (!m_ssl_options.handshake(*m_ssl_socket, boost::asio::ssl::stream_base::client, addr))
|
||||||
{
|
{
|
||||||
if (ssl_support == epee::net_utils::ssl_support_t::e_ssl_support_autodetect)
|
if (ssl_support == epee::net_utils::ssl_support_t::e_ssl_support_autodetect)
|
||||||
{
|
{
|
||||||
|
|
|
@ -108,9 +108,21 @@ namespace net_utils
|
||||||
then the handshake will not fail when peer verification fails. The
|
then the handshake will not fail when peer verification fails. The
|
||||||
assumption is that a re-connect will be attempted, so a warning is
|
assumption is that a re-connect will be attempted, so a warning is
|
||||||
logged instead of failure.
|
logged instead of failure.
|
||||||
|
|
||||||
|
\note It is strongly encouraged that clients using `system_ca`
|
||||||
|
verification provide a non-empty `host` for rfc2818 verification.
|
||||||
|
|
||||||
|
\param socket Used in SSL handshake and verification
|
||||||
|
\param type Client or server
|
||||||
|
\param host This parameter is only used when
|
||||||
|
`type == client && !host.empty()`. The value is sent to the server for
|
||||||
|
situations where multiple hostnames are being handled by a server. If
|
||||||
|
`verification == system_ca` the client also does a rfc2818 check to
|
||||||
|
ensure that the server certificate is to the provided hostname.
|
||||||
|
|
||||||
\return True if the SSL handshake completes with peer verification
|
\return True if the SSL handshake completes with peer verification
|
||||||
settings. */
|
settings. */
|
||||||
bool handshake(boost::asio::ssl::stream<boost::asio::ip::tcp::socket> &socket, boost::asio::ssl::stream_base::handshake_type type) const;
|
bool handshake(boost::asio::ssl::stream<boost::asio::ip::tcp::socket> &socket, boost::asio::ssl::stream_base::handshake_type type, const std::string& host = {}) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
// https://security.stackexchange.com/questions/34780/checking-client-hello-for-https-classification
|
// https://security.stackexchange.com/questions/34780/checking-client-hello-for-https-classification
|
||||||
|
|
|
@ -311,7 +311,7 @@ bool ssl_options_t::has_fingerprint(boost::asio::ssl::verify_context &ctx) const
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ssl_options_t::handshake(boost::asio::ssl::stream<boost::asio::ip::tcp::socket> &socket, boost::asio::ssl::stream_base::handshake_type type) const
|
bool ssl_options_t::handshake(boost::asio::ssl::stream<boost::asio::ip::tcp::socket> &socket, boost::asio::ssl::stream_base::handshake_type type, const std::string& host) const
|
||||||
{
|
{
|
||||||
socket.next_layer().set_option(boost::asio::ip::tcp::no_delay(true));
|
socket.next_layer().set_option(boost::asio::ip::tcp::no_delay(true));
|
||||||
|
|
||||||
|
@ -330,11 +330,20 @@ bool ssl_options_t::handshake(boost::asio::ssl::stream<boost::asio::ip::tcp::soc
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
socket.set_verify_mode(boost::asio::ssl::verify_peer | boost::asio::ssl::verify_fail_if_no_peer_cert);
|
socket.set_verify_mode(boost::asio::ssl::verify_peer | boost::asio::ssl::verify_fail_if_no_peer_cert);
|
||||||
socket.set_verify_callback([&](bool preverified, boost::asio::ssl::verify_context &ctx)
|
|
||||||
|
// in case server is doing "virtual" domains, set hostname
|
||||||
|
SSL* const ssl_ctx = socket.native_handle();
|
||||||
|
if (type == boost::asio::ssl::stream_base::client && !host.empty() && ssl_ctx)
|
||||||
|
SSL_set_tlsext_host_name(ssl_ctx, host.c_str());
|
||||||
|
|
||||||
|
socket.set_verify_callback([&](const bool preverified, boost::asio::ssl::verify_context &ctx)
|
||||||
{
|
{
|
||||||
// preverified means it passed system or user CA check. System CA is never loaded
|
// preverified means it passed system or user CA check. System CA is never loaded
|
||||||
// when fingerprints are whitelisted.
|
// when fingerprints are whitelisted.
|
||||||
if (!preverified && !has_fingerprint(ctx))
|
const bool verified = preverified &&
|
||||||
|
(verification != ssl_verification_t::system_ca || host.empty() || boost::asio::ssl::rfc2818_verification(host)(preverified, ctx));
|
||||||
|
|
||||||
|
if (!verified && !has_fingerprint(ctx))
|
||||||
{
|
{
|
||||||
// autodetect will reconnect without SSL - warn and keep connection encrypted
|
// autodetect will reconnect without SSL - warn and keep connection encrypted
|
||||||
if (support != ssl_support_t::e_ssl_support_autodetect)
|
if (support != ssl_support_t::e_ssl_support_autodetect)
|
||||||
|
|
Loading…
Reference in New Issue