Enabling daemon-rpc SSL now requires non-system CA verification
If `--daemon-ssl enabled` is set in the wallet, then a user certificate, fingerprint, or onion/i2p address must be provided.
This commit is contained in:
parent
d58f368289
commit
2e578b8214
|
@ -100,6 +100,9 @@ namespace net_utils
|
||||||
//! \return False iff ssl is disabled, otherwise true.
|
//! \return False iff ssl is disabled, otherwise true.
|
||||||
explicit operator bool() const noexcept { return support != ssl_support_t::e_ssl_support_disabled; }
|
explicit operator bool() const noexcept { return support != ssl_support_t::e_ssl_support_disabled; }
|
||||||
|
|
||||||
|
//! \retrurn True if `host` can be verified using `this` configuration WITHOUT system "root" CAs.
|
||||||
|
bool has_strong_verification(boost::string_ref host) const noexcept;
|
||||||
|
|
||||||
//! Search against internal fingerprints. Always false if `behavior() != user_certificate_check`.
|
//! Search against internal fingerprints. Always false if `behavior() != user_certificate_check`.
|
||||||
bool has_fingerprint(boost::asio::ssl::verify_context &ctx) const;
|
bool has_fingerprint(boost::asio::ssl::verify_context &ctx) const;
|
||||||
|
|
||||||
|
|
|
@ -278,6 +278,25 @@ bool is_ssl(const unsigned char *data, size_t len)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ssl_options_t::has_strong_verification(boost::string_ref host) const noexcept
|
||||||
|
{
|
||||||
|
// onion and i2p addresses contain information about the server cert
|
||||||
|
// which both authenticates and encrypts
|
||||||
|
if (host.ends_with(".onion") || host.ends_with(".i2p"))
|
||||||
|
return true;
|
||||||
|
switch (verification)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
case ssl_verification_t::none:
|
||||||
|
case ssl_verification_t::system_ca:
|
||||||
|
return false;
|
||||||
|
case ssl_verification_t::user_certificates:
|
||||||
|
case ssl_verification_t::user_ca:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool ssl_options_t::has_fingerprint(boost::asio::ssl::verify_context &ctx) const
|
bool ssl_options_t::has_fingerprint(boost::asio::ssl::verify_context &ctx) const
|
||||||
{
|
{
|
||||||
// can we check the certificate against a list of fingerprints?
|
// can we check the certificate against a list of fingerprints?
|
||||||
|
|
|
@ -315,6 +315,7 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl
|
||||||
const uint64_t kdf_rounds = command_line::get_arg(vm, opts.kdf_rounds);
|
const uint64_t kdf_rounds = command_line::get_arg(vm, opts.kdf_rounds);
|
||||||
THROW_WALLET_EXCEPTION_IF(kdf_rounds == 0, tools::error::wallet_internal_error, "KDF rounds must not be 0");
|
THROW_WALLET_EXCEPTION_IF(kdf_rounds == 0, tools::error::wallet_internal_error, "KDF rounds must not be 0");
|
||||||
|
|
||||||
|
const bool use_proxy = command_line::has_arg(vm, opts.proxy);
|
||||||
auto daemon_address = command_line::get_arg(vm, opts.daemon_address);
|
auto daemon_address = command_line::get_arg(vm, opts.daemon_address);
|
||||||
auto daemon_host = command_line::get_arg(vm, opts.daemon_host);
|
auto daemon_host = command_line::get_arg(vm, opts.daemon_host);
|
||||||
auto daemon_port = command_line::get_arg(vm, opts.daemon_port);
|
auto daemon_port = command_line::get_arg(vm, opts.daemon_port);
|
||||||
|
@ -382,22 +383,24 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl
|
||||||
if (daemon_address.empty())
|
if (daemon_address.empty())
|
||||||
daemon_address = std::string("http://") + daemon_host + ":" + std::to_string(daemon_port);
|
daemon_address = std::string("http://") + daemon_host + ":" + std::to_string(daemon_port);
|
||||||
|
|
||||||
boost::asio::ip::tcp::endpoint proxy{};
|
|
||||||
if (command_line::has_arg(vm, opts.proxy))
|
|
||||||
{
|
{
|
||||||
namespace ip = boost::asio::ip;
|
|
||||||
const boost::string_ref real_daemon = boost::string_ref{daemon_address}.substr(0, daemon_address.rfind(':'));
|
const boost::string_ref real_daemon = boost::string_ref{daemon_address}.substr(0, daemon_address.rfind(':'));
|
||||||
|
|
||||||
// onion and i2p addresses contain information about the server cert
|
const bool verification_required =
|
||||||
// which both authenticates and encrypts
|
ssl_options.support == epee::net_utils::ssl_support_t::e_ssl_support_enabled || use_proxy;
|
||||||
const bool unencrypted_proxy =
|
|
||||||
!real_daemon.ends_with(".onion") && !real_daemon.ends_with(".i2p") &&
|
|
||||||
daemon_ssl_ca_file.empty() && daemon_ssl_allowed_fingerprints.empty();
|
|
||||||
THROW_WALLET_EXCEPTION_IF(
|
THROW_WALLET_EXCEPTION_IF(
|
||||||
unencrypted_proxy,
|
verification_required && !ssl_options.has_strong_verification(real_daemon),
|
||||||
tools::error::wallet_internal_error,
|
tools::error::wallet_internal_error,
|
||||||
std::string{"Use of --"} + opts.proxy.name + " requires --" + opts.daemon_ssl_ca_certificates.name + " or --" + opts.daemon_ssl_allowed_fingerprints.name + " or use of a .onion/.i2p domain"
|
tools::wallet2::tr("Enabling --") + std::string{use_proxy ? opts.proxy.name : opts.daemon_ssl.name} + tools::wallet2::tr(" requires --") +
|
||||||
|
opts.daemon_ssl_ca_certificates.name + tools::wallet2::tr(" or --") + opts.daemon_ssl_allowed_fingerprints.name + tools::wallet2::tr(" or use of a .onion/.i2p domain")
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
boost::asio::ip::tcp::endpoint proxy{};
|
||||||
|
if (use_proxy)
|
||||||
|
{
|
||||||
|
namespace ip = boost::asio::ip;
|
||||||
|
|
||||||
const auto proxy_address = command_line::get_arg(vm, opts.proxy);
|
const auto proxy_address = command_line::get_arg(vm, opts.proxy);
|
||||||
|
|
||||||
|
|
|
@ -4060,13 +4060,7 @@ namespace tools
|
||||||
er.message = "Command unavailable in restricted mode.";
|
er.message = "Command unavailable in restricted mode.";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
epee::net_utils::ssl_options_t ssl_options = epee::net_utils::ssl_support_t::e_ssl_support_enabled;
|
|
||||||
if (!epee::net_utils::ssl_support_from_string(ssl_options.support, req.ssl_support))
|
|
||||||
{
|
|
||||||
er.code = WALLET_RPC_ERROR_CODE_NO_DAEMON_CONNECTION;
|
|
||||||
er.message = std::string("Invalid ssl support mode");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
std::vector<std::vector<uint8_t>> ssl_allowed_fingerprints;
|
std::vector<std::vector<uint8_t>> ssl_allowed_fingerprints;
|
||||||
ssl_allowed_fingerprints.reserve(req.ssl_allowed_fingerprints.size());
|
ssl_allowed_fingerprints.reserve(req.ssl_allowed_fingerprints.size());
|
||||||
for (const std::string &fp: req.ssl_allowed_fingerprints)
|
for (const std::string &fp: req.ssl_allowed_fingerprints)
|
||||||
|
@ -4076,15 +4070,30 @@ namespace tools
|
||||||
for (auto c: fp)
|
for (auto c: fp)
|
||||||
v.push_back(c);
|
v.push_back(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
epee::net_utils::ssl_options_t ssl_options = epee::net_utils::ssl_support_t::e_ssl_support_enabled;
|
||||||
if (req.ssl_allow_any_cert)
|
if (req.ssl_allow_any_cert)
|
||||||
ssl_options.verification = epee::net_utils::ssl_verification_t::none;
|
ssl_options.verification = epee::net_utils::ssl_verification_t::none;
|
||||||
else if (!ssl_allowed_fingerprints.empty() || !req.ssl_ca_file.empty())
|
else if (!ssl_allowed_fingerprints.empty() || !req.ssl_ca_file.empty())
|
||||||
ssl_options = epee::net_utils::ssl_options_t{std::move(ssl_allowed_fingerprints), std::move(req.ssl_ca_file)};
|
ssl_options = epee::net_utils::ssl_options_t{std::move(ssl_allowed_fingerprints), std::move(req.ssl_ca_file)};
|
||||||
|
|
||||||
|
if (!epee::net_utils::ssl_support_from_string(ssl_options.support, req.ssl_support))
|
||||||
|
{
|
||||||
|
er.code = WALLET_RPC_ERROR_CODE_NO_DAEMON_CONNECTION;
|
||||||
|
er.message = std::string("Invalid ssl support mode");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
ssl_options.auth = epee::net_utils::ssl_authentication_t{
|
ssl_options.auth = epee::net_utils::ssl_authentication_t{
|
||||||
std::move(req.ssl_private_key_path), std::move(req.ssl_certificate_path)
|
std::move(req.ssl_private_key_path), std::move(req.ssl_certificate_path)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (ssl_options.support == epee::net_utils::ssl_support_t::e_ssl_support_enabled && !ssl_options.has_strong_verification(boost::string_ref{}))
|
||||||
|
{
|
||||||
|
er.code = WALLET_RPC_ERROR_CODE_NO_DAEMON_CONNECTION;
|
||||||
|
er.message = "SSL is enabled but no user certificate or fingerprints were provided";
|
||||||
|
}
|
||||||
|
|
||||||
if (!m_wallet->set_daemon(req.address, boost::none, req.trusted, std::move(ssl_options)))
|
if (!m_wallet->set_daemon(req.address, boost::none, req.trusted, std::move(ssl_options)))
|
||||||
{
|
{
|
||||||
er.code = WALLET_RPC_ERROR_CODE_NO_DAEMON_CONNECTION;
|
er.code = WALLET_RPC_ERROR_CODE_NO_DAEMON_CONNECTION;
|
||||||
|
|
Loading…
Reference in New Issue