From a3b0284837c9ea10865e6ddeb7f1244d621ae5c0 Mon Sep 17 00:00:00 2001 From: Lee Clagett Date: Mon, 11 Mar 2019 22:01:03 -0400 Subject: [PATCH] Change SSL certificate file list to OpenSSL builtin load_verify_location Specifying SSL certificates for peer verification does an exact match, making it a not-so-obvious alias for the fingerprints option. This changes the checks to OpenSSL which loads concatenated certificate(s) from a single file and does a certificate-authority (chain of trust) check instead. There is no drop in security - a compromised exact match fingerprint has the same worse case failure. There is increased security in allowing separate long-term CA key and short-term SSL server keys. This also removes loading of the system-default CA files if a custom CA file or certificate fingerprint is specified. --- .../epee/include/net/abstract_tcp_server2.h | 6 +- .../epee/include/net/abstract_tcp_server2.inl | 8 +- contrib/epee/include/net/http_client.h | 12 +-- .../epee/include/net/http_server_impl_base.h | 4 +- contrib/epee/include/net/net_helper.h | 6 +- contrib/epee/include/net/net_ssl.h | 7 +- contrib/epee/src/net_ssl.cpp | 98 +++++++++---------- src/rpc/core_rpc_server.cpp | 24 ++--- src/rpc/core_rpc_server.h | 2 +- src/wallet/wallet2.cpp | 31 ++---- src/wallet/wallet2.h | 4 +- src/wallet/wallet_rpc_server.cpp | 20 +--- src/wallet/wallet_rpc_server_commands_defs.h | 4 +- 13 files changed, 92 insertions(+), 134 deletions(-) diff --git a/contrib/epee/include/net/abstract_tcp_server2.h b/contrib/epee/include/net/abstract_tcp_server2.h index ec08c0f4b..f28b1fb7d 100644 --- a/contrib/epee/include/net/abstract_tcp_server2.h +++ b/contrib/epee/include/net/abstract_tcp_server2.h @@ -228,8 +228,8 @@ namespace net_utils std::map server_type_map; void create_server_type_map(); - bool init_server(uint32_t port, const std::string address = "0.0.0.0", epee::net_utils::ssl_support_t ssl_support = epee::net_utils::ssl_support_t::e_ssl_support_autodetect, const std::pair &private_key_and_certificate_path = std::make_pair(std::string(), std::string()), const std::list &allowed_certificates = {}, const std::vector> &allowed_fingerprints = {}, bool allow_any_cert = false); - bool init_server(const std::string port, const std::string& address = "0.0.0.0", epee::net_utils::ssl_support_t ssl_support = epee::net_utils::ssl_support_t::e_ssl_support_autodetect, const std::pair &private_key_and_certificate_path = std::make_pair(std::string(), std::string()), const std::list &allowed_certificates = {}, const std::vector> &allowed_fingerprints = {}, bool allow_any_cert = false); + bool init_server(uint32_t port, const std::string address = "0.0.0.0", epee::net_utils::ssl_support_t ssl_support = epee::net_utils::ssl_support_t::e_ssl_support_autodetect, const std::pair &private_key_and_certificate_path = std::make_pair(std::string(), std::string()), const std::string &ca_file = {}, const std::vector> &allowed_fingerprints = {}, bool allow_any_cert = false); + bool init_server(const std::string port, const std::string& address = "0.0.0.0", epee::net_utils::ssl_support_t ssl_support = epee::net_utils::ssl_support_t::e_ssl_support_autodetect, const std::pair &private_key_and_certificate_path = std::make_pair(std::string(), std::string()), const std::string &ca_file = {}, const std::vector> &allowed_fingerprints = {}, bool allow_any_cert = false); /// Run the server's io_service loop. bool run_server(size_t threads_count, bool wait = true, const boost::thread::attributes& attrs = boost::thread::attributes()); @@ -382,8 +382,6 @@ namespace net_utils std::set connections_; ssl_context_t m_ssl_context; - std::list m_allowed_certificates; - }; // class <>boosted_tcp_server diff --git a/contrib/epee/include/net/abstract_tcp_server2.inl b/contrib/epee/include/net/abstract_tcp_server2.inl index 67c63cca5..e8b5b6c1e 100644 --- a/contrib/epee/include/net/abstract_tcp_server2.inl +++ b/contrib/epee/include/net/abstract_tcp_server2.inl @@ -941,14 +941,14 @@ PRAGMA_WARNING_DISABLE_VS(4355) } //--------------------------------------------------------------------------------- template - bool boosted_tcp_server::init_server(uint32_t port, const std::string address, epee::net_utils::ssl_support_t ssl_support, const std::pair &private_key_and_certificate_path, const std::list &allowed_certificates, const std::vector> &allowed_fingerprints, bool allow_any_cert) + bool boosted_tcp_server::init_server(uint32_t port, const std::string address, epee::net_utils::ssl_support_t ssl_support, const std::pair &private_key_and_certificate_path, const std::string &ca_file, const std::vector> &allowed_fingerprints, bool allow_any_cert) { TRY_ENTRY(); m_stop_signal_sent = false; m_port = port; m_address = address; if (ssl_support != epee::net_utils::ssl_support_t::e_ssl_support_disabled) - m_ssl_context = create_ssl_context(private_key_and_certificate_path, allowed_certificates, allowed_fingerprints, allow_any_cert); + m_ssl_context = create_ssl_context(private_key_and_certificate_path, ca_file, allowed_fingerprints, allow_any_cert); // Open the acceptor with the option to reuse the address (i.e. SO_REUSEADDR). boost::asio::ip::tcp::resolver resolver(io_service_); boost::asio::ip::tcp::resolver::query query(address, boost::lexical_cast(port), boost::asio::ip::tcp::resolver::query::canonical_name); @@ -982,7 +982,7 @@ PRAGMA_WARNING_DISABLE_VS(4355) PUSH_WARNINGS DISABLE_GCC_WARNING(maybe-uninitialized) template - bool boosted_tcp_server::init_server(const std::string port, const std::string& address, epee::net_utils::ssl_support_t ssl_support, const std::pair &private_key_and_certificate_path, const std::list &allowed_certificates, const std::vector> &allowed_fingerprints, bool allow_any_cert) + bool boosted_tcp_server::init_server(const std::string port, const std::string& address, epee::net_utils::ssl_support_t ssl_support, const std::pair &private_key_and_certificate_path, const std::string &ca_file, const std::vector> &allowed_fingerprints, bool allow_any_cert) { uint32_t p = 0; @@ -990,7 +990,7 @@ DISABLE_GCC_WARNING(maybe-uninitialized) MERROR("Failed to convert port no = " << port); return false; } - return this->init_server(p, address, ssl_support, private_key_and_certificate_path, allowed_certificates, allowed_fingerprints, allow_any_cert); + return this->init_server(p, address, ssl_support, private_key_and_certificate_path, ca_file, allowed_fingerprints, allow_any_cert); } POP_WARNINGS //--------------------------------------------------------------------------------- diff --git a/contrib/epee/include/net/http_client.h b/contrib/epee/include/net/http_client.h index 1864c77ad..36158b99f 100644 --- a/contrib/epee/include/net/http_client.h +++ b/contrib/epee/include/net/http_client.h @@ -277,7 +277,7 @@ namespace net_utils critical_section m_lock; epee::net_utils::ssl_support_t m_ssl_support; std::pair m_ssl_private_key_and_certificate_path; - std::list m_ssl_allowed_certificates; + std::string m_ssl_ca_file; std::vector> m_ssl_allowed_fingerprints; bool m_ssl_allow_any_cert; @@ -303,16 +303,16 @@ namespace net_utils const std::string &get_host() const { return m_host_buff; }; const std::string &get_port() const { return m_port; }; - bool set_server(const std::string& address, boost::optional user, epee::net_utils::ssl_support_t ssl_support = epee::net_utils::ssl_support_t::e_ssl_support_autodetect, const std::pair &private_key_and_certificate_path = {}, const std::list &allowed_ssl_certificates = {}, const std::vector> &allowed_ssl_fingerprints = {}, bool allow_any_cert = false) + bool set_server(const std::string& address, boost::optional user, epee::net_utils::ssl_support_t ssl_support = epee::net_utils::ssl_support_t::e_ssl_support_autodetect, const std::pair &private_key_and_certificate_path = {}, std::string ca_file = {}, const std::vector> &allowed_ssl_fingerprints = {}, bool allow_any_cert = false) { http::url_content parsed{}; const bool r = parse_url(address, parsed); CHECK_AND_ASSERT_MES(r, false, "failed to parse url: " << address); - set_server(std::move(parsed.host), std::to_string(parsed.port), std::move(user), ssl_support, private_key_and_certificate_path, allowed_ssl_certificates, allowed_ssl_fingerprints, allow_any_cert); + set_server(std::move(parsed.host), std::to_string(parsed.port), std::move(user), ssl_support, private_key_and_certificate_path, std::move(ca_file), allowed_ssl_fingerprints, allow_any_cert); return true; } - void set_server(std::string host, std::string port, boost::optional user, epee::net_utils::ssl_support_t ssl_support = epee::net_utils::ssl_support_t::e_ssl_support_autodetect, const std::pair &private_key_and_certificate_path = {}, const std::list &allowed_ssl_certificates = {}, const std::vector> &allowed_ssl_fingerprints = {}, bool allow_any_cert = false) + void set_server(std::string host, std::string port, boost::optional user, epee::net_utils::ssl_support_t ssl_support = epee::net_utils::ssl_support_t::e_ssl_support_autodetect, const std::pair &private_key_and_certificate_path = {}, std::string ca_file = {}, const std::vector> &allowed_ssl_fingerprints = {}, bool allow_any_cert = false) { CRITICAL_REGION_LOCAL(m_lock); disconnect(); @@ -321,10 +321,10 @@ namespace net_utils m_auth = user ? http_client_auth{std::move(*user)} : http_client_auth{}; m_ssl_support = ssl_support; m_ssl_private_key_and_certificate_path = private_key_and_certificate_path; - m_ssl_allowed_certificates = allowed_ssl_certificates; + m_ssl_ca_file = std::move(ca_file); m_ssl_allowed_fingerprints = allowed_ssl_fingerprints; m_ssl_allow_any_cert = allow_any_cert; - m_net_client.set_ssl(m_ssl_support, m_ssl_private_key_and_certificate_path, m_ssl_allowed_certificates, m_ssl_allowed_fingerprints, m_ssl_allow_any_cert); + m_net_client.set_ssl(m_ssl_support, m_ssl_private_key_and_certificate_path, m_ssl_ca_file, m_ssl_allowed_fingerprints, m_ssl_allow_any_cert); } template diff --git a/contrib/epee/include/net/http_server_impl_base.h b/contrib/epee/include/net/http_server_impl_base.h index 0922f21f2..8a6b32943 100644 --- a/contrib/epee/include/net/http_server_impl_base.h +++ b/contrib/epee/include/net/http_server_impl_base.h @@ -61,7 +61,7 @@ namespace epee boost::optional user = boost::none, epee::net_utils::ssl_support_t ssl_support = epee::net_utils::ssl_support_t::e_ssl_support_autodetect, const std::pair &private_key_and_certificate_path = {}, - std::list allowed_certificates = {}, + const std::string &ca_path = {}, std::vector> allowed_fingerprints = {}, bool allow_any_cert = false) { @@ -80,7 +80,7 @@ namespace epee m_net_server.get_config_object().m_user = std::move(user); MGINFO("Binding on " << bind_ip << ":" << bind_port); - bool res = m_net_server.init_server(bind_port, bind_ip, ssl_support, private_key_and_certificate_path, std::move(allowed_certificates), std::move(allowed_fingerprints), allow_any_cert); + bool res = m_net_server.init_server(bind_port, bind_ip, ssl_support, private_key_and_certificate_path, ca_path, std::move(allowed_fingerprints), allow_any_cert); if(!res) { LOG_ERROR("Failed to bind server"); diff --git a/contrib/epee/include/net/net_helper.h b/contrib/epee/include/net/net_helper.h index aa3df7160..2b220bb0f 100644 --- a/contrib/epee/include/net/net_helper.h +++ b/contrib/epee/include/net/net_helper.h @@ -136,12 +136,12 @@ namespace net_utils catch(...) { /* ignore */ } } - inline void set_ssl(epee::net_utils::ssl_support_t ssl_support = epee::net_utils::ssl_support_t::e_ssl_support_autodetect, const std::pair &private_key_and_certificate_path = {}, std::list allowed_certificates = {}, std::vector> allowed_fingerprints = {}, bool allow_any_cert = false) + inline void set_ssl(epee::net_utils::ssl_support_t ssl_support = epee::net_utils::ssl_support_t::e_ssl_support_autodetect, const std::pair &private_key_and_certificate_path = {}, const std::string &ca_path = {}, std::vector> allowed_fingerprints = {}, bool allow_any_cert = false) { if (ssl_support == epee::net_utils::ssl_support_t::e_ssl_support_disabled) m_ctx = {boost::asio::ssl::context(boost::asio::ssl::context::tlsv12), {}, {}}; else - m_ctx = create_ssl_context(private_key_and_certificate_path, std::move(allowed_certificates), std::move(allowed_fingerprints), allow_any_cert); + m_ctx = create_ssl_context(private_key_and_certificate_path, ca_path, std::move(allowed_fingerprints), allow_any_cert); m_ssl_support = ssl_support; } @@ -212,8 +212,6 @@ namespace net_utils // Set SSL options // disable sslv2 - m_ctx.context.set_options(boost::asio::ssl::context::default_workarounds | boost::asio::ssl::context::no_sslv2); - m_ctx.context.set_default_verify_paths(); m_ssl_socket.reset(new boost::asio::ssl::stream(m_io_service, m_ctx.context)); // Get a list of endpoints corresponding to the server name. diff --git a/contrib/epee/include/net/net_ssl.h b/contrib/epee/include/net/net_ssl.h index f7b102164..1b90a948d 100644 --- a/contrib/epee/include/net/net_ssl.h +++ b/contrib/epee/include/net/net_ssl.h @@ -31,10 +31,11 @@ #include #include -#include +#include #include #include #include +#include namespace epee { @@ -49,7 +50,7 @@ namespace net_utils struct ssl_context_t { boost::asio::ssl::context context; - std::list allowed_certificates; + std::string ca_path; std::vector> allowed_fingerprints; bool allow_any_cert; }; @@ -57,7 +58,7 @@ namespace net_utils // https://security.stackexchange.com/questions/34780/checking-client-hello-for-https-classification constexpr size_t get_ssl_magic_size() { return 9; } bool is_ssl(const unsigned char *data, size_t len); - ssl_context_t create_ssl_context(const std::pair &private_key_and_certificate_path, std::list allowed_certificates, std::vector> allowed_fingerprints, bool allow_any_cert); + ssl_context_t create_ssl_context(const std::pair &private_key_and_certificate_path, const std::string &ca_path, std::vector> allowed_fingerprints, bool allow_any_cert); void use_ssl_certificate(ssl_context_t &ssl_context, const std::pair &private_key_and_certificate_path); bool is_certificate_allowed(boost::asio::ssl::verify_context &ctx, const ssl_context_t &ssl_context); bool ssl_handshake(boost::asio::ssl::stream &socket, boost::asio::ssl::stream_base::handshake_type type, const epee::net_utils::ssl_context_t &ssl_context); diff --git a/contrib/epee/src/net_ssl.cpp b/contrib/epee/src/net_ssl.cpp index cb65121bd..1caed53ea 100644 --- a/contrib/epee/src/net_ssl.cpp +++ b/contrib/epee/src/net_ssl.cpp @@ -77,6 +77,21 @@ namespace } }; using openssl_bignum = std::unique_ptr; + + boost::system::error_code load_ca_file(boost::asio::ssl::context& ctx, const std::string& path) + { + SSL_CTX* const ssl_ctx = ctx.native_handle(); // could be moved from context + if (ssl_ctx == nullptr) + return {boost::asio::error::invalid_argument}; + + if (!SSL_CTX_load_verify_locations(ssl_ctx, path.c_str(), nullptr)) + { + return boost::system::error_code{ + int(::ERR_get_error()), boost::asio::error::get_ssl_category() + }; + } + return boost::system::error_code{}; + } } namespace epee @@ -84,6 +99,7 @@ namespace epee namespace net_utils { + // https://stackoverflow.com/questions/256405/programmatically-create-x509-certificate-using-openssl bool create_ssl_certificate(EVP_PKEY *&pkey, X509 *&cert) { @@ -155,9 +171,9 @@ bool create_ssl_certificate(EVP_PKEY *&pkey, X509 *&cert) return true; } -ssl_context_t create_ssl_context(const std::pair &private_key_and_certificate_path, std::list allowed_certificates, std::vector> allowed_fingerprints, bool allow_any_cert) +ssl_context_t create_ssl_context(const std::pair &private_key_and_certificate_path, const std::string &ca_path, std::vector> allowed_fingerprints, bool allow_any_cert) { - ssl_context_t ssl_context{boost::asio::ssl::context(boost::asio::ssl::context::tlsv12), std::move(allowed_certificates), std::move(allowed_fingerprints)}; + ssl_context_t ssl_context{boost::asio::ssl::context(boost::asio::ssl::context::tlsv12), std::move(ca_path), std::move(allowed_fingerprints)}; // only allow tls v1.2 and up ssl_context.context.set_options(boost::asio::ssl::context::default_workarounds); @@ -186,7 +202,15 @@ ssl_context_t create_ssl_context(const std::pair &priv #ifdef SSL_OP_NO_COMPRESSION SSL_CTX_set_options(ctx, SSL_OP_NO_COMPRESSION); #endif - ssl_context.context.set_default_verify_paths(); + + if (!ssl_context.ca_path.empty()) + { + const boost::system::error_code err = load_ca_file(ssl_context.context, ssl_context.ca_path); + if (err) + throw boost::system::system_error{err, "Failed to load user CA file at " + ssl_context.ca_path}; + } + else if (allowed_fingerprints.empty()) + ssl_context.context.set_default_verify_paths(); // only use system-CAs if no user-supplied cert info CHECK_AND_ASSERT_THROW_MES(private_key_and_certificate_path.first.empty() == private_key_and_certificate_path.second.empty(), "private key and certificate must be either both given or both empty"); if (private_key_and_certificate_path.second.empty()) @@ -237,21 +261,21 @@ bool is_ssl(const unsigned char *data, size_t len) bool is_certificate_allowed(boost::asio::ssl::verify_context &ctx, const ssl_context_t &ssl_context) { - X509_STORE_CTX *sctx = ctx.native_handle(); - if (!sctx) - { - MERROR("Error getting verify_context handle"); - return false; - } - X509 *cert =X509_STORE_CTX_get_current_cert(sctx); - if (!cert) - { - MERROR("No certificate found in verify_context"); - return false; - } - // can we check the certificate against a list of fingerprints? if (!ssl_context.allowed_fingerprints.empty()) { + X509_STORE_CTX *sctx = ctx.native_handle(); + if (!sctx) + { + MERROR("Error getting verify_context handle"); + return false; + } + X509 *cert =X509_STORE_CTX_get_current_cert(sctx); + if (!cert) + { + MERROR("No certificate found in verify_context"); + return false; + } + // buffer for the certificate digest and the size of the result std::vector digest(EVP_MAX_MD_SIZE); unsigned int size{ 0 }; @@ -270,52 +294,20 @@ bool is_certificate_allowed(boost::asio::ssl::verify_context &ctx, const ssl_con return true; } - if (!ssl_context.allowed_certificates.empty()) { - BIO *bio_cert = BIO_new(BIO_s_mem()); - bool success = PEM_write_bio_X509(bio_cert, cert); - if (!success) - { - BIO_free(bio_cert); - MERROR("Failed to print certificate"); - return false; - } - BUF_MEM *buf = NULL; - BIO_get_mem_ptr(bio_cert, &buf); - if (!buf || !buf->data || !buf->length) - { - BIO_free(bio_cert); - MERROR("Failed to write certificate: " << ERR_get_error()); - return false; - } - std::string certificate(std::string(buf->data, buf->length)); - BIO_free(bio_cert); - if (std::find(ssl_context.allowed_certificates.begin(), ssl_context.allowed_certificates.end(), certificate) != ssl_context.allowed_certificates.end()) - return true; - } - - // if either checklist is non-empty we must have failed it - return ssl_context.allowed_fingerprints.empty() && ssl_context.allowed_certificates.empty(); + return ssl_context.allowed_fingerprints.empty() && ssl_context.ca_path.empty(); } bool ssl_handshake(boost::asio::ssl::stream &socket, boost::asio::ssl::stream_base::handshake_type type, const epee::net_utils::ssl_context_t &ssl_context) { bool verified = false; socket.next_layer().set_option(boost::asio::ip::tcp::no_delay(true)); + socket.set_verify_mode(boost::asio::ssl::verify_peer); socket.set_verify_callback([&](bool preverified, boost::asio::ssl::verify_context &ctx) { - if (!preverified) - { - const int err = X509_STORE_CTX_get_error(ctx.native_handle()); - const int depth = X509_STORE_CTX_get_error_depth(ctx.native_handle()); - if (err != X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT || depth != 0) - { - MERROR("Invalid SSL certificate, error " << err << " at depth " << depth << ", connection dropped"); - return false; - } - } - if (!ssl_context.allow_any_cert && !is_certificate_allowed(ctx, ssl_context)) - { + // preverified means it passed system or user CA check. System CA is never loaded + // when fingerprints are whitelisted. + if (!preverified && !ssl_context.allow_any_cert && !is_certificate_allowed(ctx, ssl_context)) { MERROR("Certificate is not in the allowed list, connection droppped"); return false; } diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 3e580a0fb..6273feaf4 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -91,7 +91,7 @@ namespace cryptonote command_line::add_arg(desc, arg_rpc_ssl); command_line::add_arg(desc, arg_rpc_ssl_private_key); command_line::add_arg(desc, arg_rpc_ssl_certificate); - command_line::add_arg(desc, arg_rpc_ssl_allowed_certificates); + command_line::add_arg(desc, arg_rpc_ssl_ca_certificates); command_line::add_arg(desc, arg_rpc_ssl_allowed_fingerprints); command_line::add_arg(desc, arg_rpc_ssl_allow_any_cert); command_line::add_arg(desc, arg_bootstrap_daemon_address); @@ -158,17 +158,7 @@ namespace cryptonote } const std::string ssl_private_key = command_line::get_arg(vm, arg_rpc_ssl_private_key); const std::string ssl_certificate = command_line::get_arg(vm, arg_rpc_ssl_certificate); - const std::vector ssl_allowed_certificate_paths = command_line::get_arg(vm, arg_rpc_ssl_allowed_certificates); - std::list ssl_allowed_certificates; - for (const std::string &path: ssl_allowed_certificate_paths) - { - ssl_allowed_certificates.push_back({}); - if (!epee::file_io_utils::load_file_to_string(path, ssl_allowed_certificates.back())) - { - MERROR("Failed to load certificate: " << path); - ssl_allowed_certificates.back() = std::string(); - } - } + std::string ssl_ca_path = command_line::get_arg(vm, arg_rpc_ssl_ca_certificates); const std::vector ssl_allowed_fingerprint_strings = command_line::get_arg(vm, arg_rpc_ssl_allowed_fingerprints); std::vector> ssl_allowed_fingerprints{ ssl_allowed_fingerprint_strings.size() }; @@ -178,7 +168,7 @@ namespace cryptonote auto rng = [](size_t len, uint8_t *ptr){ return crypto::rand(len, ptr); }; return epee::http_server_impl_base::init( rng, std::move(port), std::move(rpc_config->bind_ip), std::move(rpc_config->access_control_origins), std::move(http_login), - ssl_support, std::make_pair(ssl_private_key, ssl_certificate), std::move(ssl_allowed_certificates), std::move(ssl_allowed_fingerprints), ssl_allow_any_cert + ssl_support, std::make_pair(ssl_private_key, ssl_certificate), std::move(ssl_ca_path), std::move(ssl_allowed_fingerprints), ssl_allow_any_cert ); } //------------------------------------------------------------------------------------------------------------------------------ @@ -2408,9 +2398,9 @@ namespace cryptonote , "" }; - const command_line::arg_descriptor> core_rpc_server::arg_rpc_ssl_allowed_certificates = { - "rpc-ssl-allowed-certificates" - , "List of paths to PEM format certificates of allowed peers (all allowed if empty)" + const command_line::arg_descriptor core_rpc_server::arg_rpc_ssl_ca_certificates = { + "rpc-ssl-ca-certificates" + , "Path to file containing concatenated PEM format certificate(s) to replace system CA(s)." }; const command_line::arg_descriptor> core_rpc_server::arg_rpc_ssl_allowed_fingerprints = { @@ -2420,7 +2410,7 @@ namespace cryptonote const command_line::arg_descriptor core_rpc_server::arg_rpc_ssl_allow_any_cert = { "rpc-ssl-allow-any-cert" - , "Allow any peer certificate, rather than just those on the allowed list" + , "Allow any peer certificate" , false }; diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h index 8f5d83f1b..a42ca2494 100644 --- a/src/rpc/core_rpc_server.h +++ b/src/rpc/core_rpc_server.h @@ -60,7 +60,7 @@ namespace cryptonote static const command_line::arg_descriptor arg_rpc_ssl; static const command_line::arg_descriptor arg_rpc_ssl_private_key; static const command_line::arg_descriptor arg_rpc_ssl_certificate; - static const command_line::arg_descriptor> arg_rpc_ssl_allowed_certificates; + static const command_line::arg_descriptor arg_rpc_ssl_ca_certificates; static const command_line::arg_descriptor> arg_rpc_ssl_allowed_fingerprints; static const command_line::arg_descriptor arg_rpc_ssl_allow_any_cert; static const command_line::arg_descriptor arg_bootstrap_daemon_address; diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 54fbd9ca8..c4f466699 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -243,7 +243,7 @@ struct options { const command_line::arg_descriptor daemon_ssl = {"daemon-ssl", tools::wallet2::tr("Enable SSL on daemon RPC connections: enabled|disabled|autodetect"), "autodetect"}; const command_line::arg_descriptor daemon_ssl_private_key = {"daemon-ssl-private-key", tools::wallet2::tr("Path to a PEM format private key"), ""}; const command_line::arg_descriptor daemon_ssl_certificate = {"daemon-ssl-certificate", tools::wallet2::tr("Path to a PEM format certificate"), ""}; - const command_line::arg_descriptor> daemon_ssl_allowed_certificates = {"daemon-ssl-allowed-certificates", tools::wallet2::tr("List of paths to PEM format certificates of allowed RPC servers")}; + const command_line::arg_descriptor daemon_ssl_ca_certificates = {"daemon-ssl-ca-certificates", tools::wallet2::tr("Path to file containing concatenated PEM format certificate(s) to replace system CA(s).")}; const command_line::arg_descriptor> daemon_ssl_allowed_fingerprints = {"daemon-ssl-allowed-fingerprints", tools::wallet2::tr("List of valid fingerprints of allowed RPC servers")}; const command_line::arg_descriptor daemon_ssl_allow_any_cert = {"daemon-ssl-allow-any-cert", tools::wallet2::tr("Allow any SSL certificate from the daemon"), false}; const command_line::arg_descriptor testnet = {"testnet", tools::wallet2::tr("For testnet. Daemon must also be launched with --testnet flag"), false}; @@ -321,7 +321,7 @@ std::unique_ptr make_basic(const boost::program_options::variabl auto device_derivation_path = command_line::get_arg(vm, opts.hw_device_derivation_path); auto daemon_ssl_private_key = command_line::get_arg(vm, opts.daemon_ssl_private_key); auto daemon_ssl_certificate = command_line::get_arg(vm, opts.daemon_ssl_certificate); - auto daemon_ssl_allowed_certificates = command_line::get_arg(vm, opts.daemon_ssl_allowed_certificates); + auto daemon_ssl_ca_file = command_line::get_arg(vm, opts.daemon_ssl_ca_certificates); auto daemon_ssl_allowed_fingerprints = command_line::get_arg(vm, opts.daemon_ssl_allowed_fingerprints); auto daemon_ssl_allow_any_cert = command_line::get_arg(vm, opts.daemon_ssl_allow_any_cert); auto daemon_ssl = command_line::get_arg(vm, opts.daemon_ssl); @@ -367,11 +367,11 @@ std::unique_ptr make_basic(const boost::program_options::variabl // which both authenticates and encrypts const bool unencrypted_proxy = !real_daemon.ends_with(".onion") && !real_daemon.ends_with(".i2p") && - daemon_ssl_allowed_certificates.empty() && daemon_ssl_allowed_fingerprints.empty(); + daemon_ssl_ca_file.empty() && daemon_ssl_allowed_fingerprints.empty(); THROW_WALLET_EXCEPTION_IF( unencrypted_proxy, tools::error::wallet_internal_error, - std::string{"Use of --"} + opts.proxy.name + " requires --" + opts.daemon_ssl_allowed_certificates.name + " or --" + opts.daemon_ssl_allowed_fingerprints.name + " or use of a .onion/.i2p domain" + 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" ); const auto proxy_address = command_line::get_arg(vm, opts.proxy); @@ -416,22 +416,11 @@ std::unique_ptr make_basic(const boost::program_options::variabl catch (const std::exception &e) { } } - std::list ssl_allowed_certificates; - for (const std::string &path: daemon_ssl_allowed_certificates) - { - ssl_allowed_certificates.push_back({}); - if (!epee::file_io_utils::load_file_to_string(path, ssl_allowed_certificates.back())) - { - MERROR("Failed to load certificate: " << path); - ssl_allowed_certificates.back() = std::string(); - } - } - std::vector> ssl_allowed_fingerprints{ daemon_ssl_allowed_fingerprints.size() }; std::transform(daemon_ssl_allowed_fingerprints.begin(), daemon_ssl_allowed_fingerprints.end(), ssl_allowed_fingerprints.begin(), epee::from_hex::vector); std::unique_ptr wallet(new tools::wallet2(nettype, kdf_rounds, unattended)); - wallet->init(std::move(daemon_address), std::move(login), std::move(proxy), 0, *trusted_daemon, ssl_support, std::make_pair(daemon_ssl_private_key, daemon_ssl_certificate), ssl_allowed_certificates, ssl_allowed_fingerprints, daemon_ssl_allow_any_cert); + wallet->init(std::move(daemon_address), std::move(login), std::move(proxy), 0, *trusted_daemon, ssl_support, std::make_pair(daemon_ssl_private_key, daemon_ssl_certificate), std::move(daemon_ssl_ca_file), ssl_allowed_fingerprints, daemon_ssl_allow_any_cert); boost::filesystem::path ringdb_path = command_line::get_arg(vm, opts.shared_ringdb_dir); wallet->set_ring_database(ringdb_path.string()); wallet->get_message_store().set_options(vm); @@ -1100,7 +1089,7 @@ void wallet2::init_options(boost::program_options::options_description& desc_par command_line::add_arg(desc_params, opts.daemon_ssl); command_line::add_arg(desc_params, opts.daemon_ssl_private_key); command_line::add_arg(desc_params, opts.daemon_ssl_certificate); - command_line::add_arg(desc_params, opts.daemon_ssl_allowed_certificates); + command_line::add_arg(desc_params, opts.daemon_ssl_ca_certificates); command_line::add_arg(desc_params, opts.daemon_ssl_allowed_fingerprints); command_line::add_arg(desc_params, opts.daemon_ssl_allow_any_cert); command_line::add_arg(desc_params, opts.testnet); @@ -1156,7 +1145,7 @@ std::unique_ptr wallet2::make_dummy(const boost::program_options::varia //---------------------------------------------------------------------------------------------------- bool wallet2::set_daemon(std::string daemon_address, boost::optional daemon_login, bool trusted_daemon, epee::net_utils::ssl_support_t ssl_support, const std::pair &private_key_and_certificate_path, - const std::list &allowed_certificates, const std::vector> &allowed_fingerprints, + std::string ca_file, const std::vector> &allowed_fingerprints, bool allow_any_cert) { if(m_http_client.is_connected()) @@ -1166,17 +1155,17 @@ bool wallet2::set_daemon(std::string daemon_address, boost::optional daemon_login, boost::asio::ip::tcp::endpoint proxy, uint64_t upper_transaction_weight_limit, bool trusted_daemon, epee::net_utils::ssl_support_t ssl_support, const std::pair &private_key_and_certificate_path, const std::list &allowed_certificates, const std::vector> &allowed_fingerprints, bool allow_any_cert) +bool wallet2::init(std::string daemon_address, boost::optional daemon_login, boost::asio::ip::tcp::endpoint proxy, uint64_t upper_transaction_weight_limit, bool trusted_daemon, epee::net_utils::ssl_support_t ssl_support, const std::pair &private_key_and_certificate_path, std::string ca_file, const std::vector> &allowed_fingerprints, bool allow_any_cert) { m_checkpoints.init_default_checkpoints(m_nettype); m_is_initialized = true; m_upper_transaction_weight_limit = upper_transaction_weight_limit; if (proxy != boost::asio::ip::tcp::endpoint{}) m_http_client.set_connector(net::socks::connector{std::move(proxy)}); - return set_daemon(daemon_address, daemon_login, trusted_daemon, ssl_support, private_key_and_certificate_path, allowed_certificates, allowed_fingerprints, allow_any_cert); + return set_daemon(daemon_address, daemon_login, trusted_daemon, ssl_support, private_key_and_certificate_path, std::move(ca_file), allowed_fingerprints, allow_any_cert); } //---------------------------------------------------------------------------------------------------- bool wallet2::is_deterministic() const diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index a24127800..58dc5f082 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -691,13 +691,13 @@ namespace tools bool trusted_daemon = true, epee::net_utils::ssl_support_t ssl_support = epee::net_utils::ssl_support_t::e_ssl_support_autodetect, const std::pair &private_key_and_certificate_path = {}, - const std::list &allowed_certificates = {}, const std::vector> &allowed_fingerprints = {}, + std::string ca_file = {}, const std::vector> &allowed_fingerprints = {}, bool allow_any_cert = false); bool set_daemon(std::string daemon_address = "http://localhost:8080", boost::optional daemon_login = boost::none, bool trusted_daemon = true, epee::net_utils::ssl_support_t ssl_support = epee::net_utils::ssl_support_t::e_ssl_support_autodetect, const std::pair &private_key_and_certificate_path = {}, - const std::list &allowed_certificates = {}, const std::vector> &allowed_fingerprints = {}, + std::string ca_file = {}, const std::vector> &allowed_fingerprints = {}, bool allow_any_cert = false); void stop() { m_run.store(false, std::memory_order_relaxed); m_message_store.stop(); } diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 92265d954..7824ed62e 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -68,7 +68,7 @@ namespace const command_line::arg_descriptor arg_rpc_ssl = {"rpc-ssl", tools::wallet2::tr("Enable SSL on wallet RPC connections: enabled|disabled|autodetect"), "autodetect"}; const command_line::arg_descriptor arg_rpc_ssl_private_key = {"rpc-ssl-private-key", tools::wallet2::tr("Path to a PEM format private key"), ""}; const command_line::arg_descriptor arg_rpc_ssl_certificate = {"rpc-ssl-certificate", tools::wallet2::tr("Path to a PEM format certificate"), ""}; - const command_line::arg_descriptor> arg_rpc_ssl_allowed_certificates = {"rpc-ssl-allowed-certificates", tools::wallet2::tr("List of paths to PEM format certificates of allowed RPC servers (all allowed if empty)")}; + const command_line::arg_descriptor arg_rpc_ssl_ca_certificates = {"rpc-ssl-ca-certificates", tools::wallet2::tr("Path to file containing concatenated PEM format certificate(s) to replace system CA(s).")}; const command_line::arg_descriptor> arg_rpc_ssl_allowed_fingerprints = {"rpc-ssl-allowed-fingerprints", tools::wallet2::tr("List of certificate fingerprints to allow")}; constexpr const char default_rpc_username[] = "monero"; @@ -247,7 +247,7 @@ namespace tools auto rpc_ssl_private_key = command_line::get_arg(vm, arg_rpc_ssl_private_key); auto rpc_ssl_certificate = command_line::get_arg(vm, arg_rpc_ssl_certificate); - auto rpc_ssl_allowed_certificates = command_line::get_arg(vm, arg_rpc_ssl_allowed_certificates); + auto rpc_ssl_ca_file = command_line::get_arg(vm, arg_rpc_ssl_ca_certificates); auto rpc_ssl_allowed_fingerprints = command_line::get_arg(vm, arg_rpc_ssl_allowed_fingerprints); auto rpc_ssl = command_line::get_arg(vm, arg_rpc_ssl); epee::net_utils::ssl_support_t rpc_ssl_support; @@ -256,16 +256,6 @@ namespace tools MERROR("Invalid argument for " << std::string(arg_rpc_ssl.name)); return false; } - std::list allowed_certificates; - for (const std::string &path: rpc_ssl_allowed_certificates) - { - allowed_certificates.push_back({}); - if (!epee::file_io_utils::load_file_to_string(path, allowed_certificates.back())) - { - MERROR("Failed to load certificate: " << path); - allowed_certificates.back() = std::string(); - } - } std::vector> allowed_fingerprints{ rpc_ssl_allowed_fingerprints.size() }; std::transform(rpc_ssl_allowed_fingerprints.begin(), rpc_ssl_allowed_fingerprints.end(), allowed_fingerprints.begin(), epee::from_hex::vector); @@ -277,7 +267,7 @@ namespace tools auto rng = [](size_t len, uint8_t *ptr) { return crypto::rand(len, ptr); }; return epee::http_server_impl_base::init( rng, std::move(bind_port), std::move(rpc_config->bind_ip), std::move(rpc_config->access_control_origins), std::move(http_login), - rpc_ssl_support, std::make_pair(rpc_ssl_private_key, rpc_ssl_certificate), std::move(allowed_certificates), std::move(allowed_fingerprints) + rpc_ssl_support, std::make_pair(rpc_ssl_private_key, rpc_ssl_certificate), std::move(rpc_ssl_ca_file), std::move(allowed_fingerprints) ); } //------------------------------------------------------------------------------------------------------------------------------ @@ -4070,7 +4060,7 @@ namespace tools for (auto c: fp) v.push_back(c); } - if (!m_wallet->set_daemon(req.address, boost::none, req.trusted, ssl_support, std::make_pair(req.ssl_private_key_path, req.ssl_certificate_path), req.ssl_allowed_certificates, ssl_allowed_fingerprints, req.ssl_allow_any_cert)) + if (!m_wallet->set_daemon(req.address, boost::none, req.trusted, ssl_support, std::make_pair(req.ssl_private_key_path, req.ssl_certificate_path), std::move(req.ssl_ca_file), ssl_allowed_fingerprints, req.ssl_allow_any_cert)) { er.code = WALLET_RPC_ERROR_CODE_NO_DAEMON_CONNECTION; er.message = std::string("Unable to set daemon"); @@ -4278,7 +4268,7 @@ int main(int argc, char** argv) { command_line::add_arg(desc_params, arg_rpc_ssl); command_line::add_arg(desc_params, arg_rpc_ssl_private_key); command_line::add_arg(desc_params, arg_rpc_ssl_certificate); - command_line::add_arg(desc_params, arg_rpc_ssl_allowed_certificates); + command_line::add_arg(desc_params, arg_rpc_ssl_ca_certificates); command_line::add_arg(desc_params, arg_rpc_ssl_allowed_fingerprints); daemonizer::init_options(hidden_options, desc_params); diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h index 7984f6584..4c945ab41 100644 --- a/src/wallet/wallet_rpc_server_commands_defs.h +++ b/src/wallet/wallet_rpc_server_commands_defs.h @@ -2448,7 +2448,7 @@ namespace wallet_rpc std::string ssl_support; // disabled, enabled, autodetect std::string ssl_private_key_path; std::string ssl_certificate_path; - std::list ssl_allowed_certificates; + std::string ssl_ca_file; std::vector ssl_allowed_fingerprints; bool ssl_allow_any_cert; @@ -2458,7 +2458,7 @@ namespace wallet_rpc KV_SERIALIZE_OPT(ssl_support, (std::string)"autodetect") KV_SERIALIZE(ssl_private_key_path) KV_SERIALIZE(ssl_certificate_path) - KV_SERIALIZE(ssl_allowed_certificates) + KV_SERIALIZE(ssl_ca_file) KV_SERIALIZE(ssl_allowed_fingerprints) KV_SERIALIZE_OPT(ssl_allow_any_cert, false) END_KV_SERIALIZE_MAP()