diff options
33 files changed, 744 insertions, 496 deletions
@@ -175,9 +175,13 @@ library archives (`.a`). build the library binary manually. This can be done with the following command ```sudo apt-get install libgtest-dev && cd /usr/src/gtest && sudo cmake . && sudo make && sudo mv libg* /usr/lib/ ``` [2] libnorm-dev is needed if your zmq library was built with libnorm, and not needed otherwise -Debian / Ubuntu one liner for all dependencies +Install all dependencies at once on Debian/Ubuntu: + ``` sudo apt update && sudo apt install build-essential cmake pkg-config libboost-all-dev libssl-dev libzmq3-dev libunbound-dev libsodium-dev libunwind8-dev liblzma-dev libreadline6-dev libldns-dev libexpat1-dev doxygen graphviz libpgm-dev``` +Install all dependencies at once on macOS: +``` brew update && brew install cmake pkg-config openssl boost hidapi zmq libpgm unbound libsodium miniupnpc readline ldns expat doxygen graphviz protobuf ``` + FreeBSD one liner for required to build dependencies ```pkg install git gmake cmake pkgconf boost-libs cppzmq libsodium``` diff --git a/contrib/epee/include/net/abstract_tcp_server2.h b/contrib/epee/include/net/abstract_tcp_server2.h index ec08c0f4b..d0eabbba5 100644 --- a/contrib/epee/include/net/abstract_tcp_server2.h +++ b/contrib/epee/include/net/abstract_tcp_server2.h @@ -90,10 +90,10 @@ namespace net_utils public: typedef typename t_protocol_handler::connection_context t_connection_context; - struct shared_state : socket_stats + struct shared_state : connection_basic_shared_state { shared_state() - : socket_stats(), pfilter(nullptr), config() + : connection_basic_shared_state(), pfilter(nullptr), config() {} i_connection_filter* pfilter; @@ -104,14 +104,12 @@ namespace net_utils explicit connection( boost::asio::io_service& io_service, boost::shared_ptr<shared_state> state, t_connection_type connection_type, - epee::net_utils::ssl_support_t ssl_support, - ssl_context_t &ssl_context); + epee::net_utils::ssl_support_t ssl_support); explicit connection( boost::asio::ip::tcp::socket&& sock, boost::shared_ptr<shared_state> state, t_connection_type connection_type, - epee::net_utils::ssl_support_t ssl_support, - ssl_context_t &ssl_context); + epee::net_utils::ssl_support_t ssl_support); @@ -228,8 +226,8 @@ namespace net_utils std::map<std::string, t_connection_type> 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<std::string, std::string> &private_key_and_certificate_path = std::make_pair(std::string(), std::string()), const std::list<std::string> &allowed_certificates = {}, const std::vector<std::vector<uint8_t>> &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<std::string, std::string> &private_key_and_certificate_path = std::make_pair(std::string(), std::string()), const std::list<std::string> &allowed_certificates = {}, const std::vector<std::vector<uint8_t>> &allowed_fingerprints = {}, bool allow_any_cert = false); + bool init_server(uint32_t port, const std::string address = "0.0.0.0", ssl_options_t ssl_options = ssl_support_t::e_ssl_support_autodetect); + bool init_server(const std::string port, const std::string& address = "0.0.0.0", ssl_options_t ssl_options = ssl_support_t::e_ssl_support_autodetect); /// 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()); @@ -380,10 +378,6 @@ namespace net_utils boost::mutex connections_mutex; std::set<connection_ptr> connections_; - - ssl_context_t m_ssl_context; - std::list<std::string> 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..58f899a73 100644 --- a/contrib/epee/include/net/abstract_tcp_server2.inl +++ b/contrib/epee/include/net/abstract_tcp_server2.inl @@ -80,10 +80,9 @@ PRAGMA_WARNING_DISABLE_VS(4355) connection<t_protocol_handler>::connection( boost::asio::io_service& io_service, boost::shared_ptr<shared_state> state, t_connection_type connection_type, - epee::net_utils::ssl_support_t ssl_support, - ssl_context_t &ssl_context + ssl_support_t ssl_support ) - : connection(boost::asio::ip::tcp::socket{io_service}, std::move(state), connection_type, ssl_support, ssl_context) + : connection(boost::asio::ip::tcp::socket{io_service}, std::move(state), connection_type, ssl_support) { } @@ -91,11 +90,10 @@ PRAGMA_WARNING_DISABLE_VS(4355) connection<t_protocol_handler>::connection( boost::asio::ip::tcp::socket&& sock, boost::shared_ptr<shared_state> state, t_connection_type connection_type, - epee::net_utils::ssl_support_t ssl_support, - ssl_context_t &ssl_context + ssl_support_t ssl_support ) : - connection_basic(std::move(sock), state, ssl_support, ssl_context), + connection_basic(std::move(sock), state, ssl_support), m_protocol_handler(this, check_and_get(state).config, context), m_connection_type( connection_type ), m_throttle_speed_in("speed_in", "throttle_speed_in"), @@ -176,9 +174,9 @@ PRAGMA_WARNING_DISABLE_VS(4355) _dbg3("[sock " << socket_.native_handle() << "] new connection from " << print_connection_context_short(context) << " to " << local_ep.address().to_string() << ':' << local_ep.port() << - ", total sockets objects " << get_stats().sock_count); + ", total sockets objects " << get_state().sock_count); - if(static_cast<shared_state&>(get_stats()).pfilter && !static_cast<shared_state&>(get_stats()).pfilter->is_remote_host_allowed(context.m_remote_address)) + if(static_cast<shared_state&>(get_state()).pfilter && !static_cast<shared_state&>(get_state()).pfilter->is_remote_host_allowed(context.m_remote_address)) { _dbg2("[sock " << socket().native_handle() << "] host denied " << context.m_remote_address.host_str() << ", shutdowning connection"); close(); @@ -901,8 +899,7 @@ PRAGMA_WARNING_DISABLE_VS(4355) m_threads_count(0), m_thread_index(0), m_connection_type( connection_type ), - new_connection_(), - m_ssl_context({boost::asio::ssl::context(boost::asio::ssl::context::tlsv12), {}}) + new_connection_() { create_server_type_map(); m_thread_name_prefix = "NET"; @@ -918,8 +915,7 @@ PRAGMA_WARNING_DISABLE_VS(4355) m_threads_count(0), m_thread_index(0), m_connection_type(connection_type), - new_connection_(), - m_ssl_context({boost::asio::ssl::context(boost::asio::ssl::context::sslv23), {}}) + new_connection_() { create_server_type_map(); m_thread_name_prefix = "NET"; @@ -941,14 +937,14 @@ PRAGMA_WARNING_DISABLE_VS(4355) } //--------------------------------------------------------------------------------- template<class t_protocol_handler> - bool boosted_tcp_server<t_protocol_handler>::init_server(uint32_t port, const std::string address, epee::net_utils::ssl_support_t ssl_support, const std::pair<std::string, std::string> &private_key_and_certificate_path, const std::list<std::string> &allowed_certificates, const std::vector<std::vector<uint8_t>> &allowed_fingerprints, bool allow_any_cert) + bool boosted_tcp_server<t_protocol_handler>::init_server(uint32_t port, const std::string address, ssl_options_t ssl_options) { 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); + if (ssl_options) + m_state->configure_ssl(std::move(ssl_options)); // 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<std::string>(port), boost::asio::ip::tcp::resolver::query::canonical_name); @@ -960,7 +956,7 @@ PRAGMA_WARNING_DISABLE_VS(4355) boost::asio::ip::tcp::endpoint binded_endpoint = acceptor_.local_endpoint(); m_port = binded_endpoint.port(); MDEBUG("start accept"); - new_connection_.reset(new connection<t_protocol_handler>(io_service_, m_state, m_connection_type, ssl_support, m_ssl_context)); + new_connection_.reset(new connection<t_protocol_handler>(io_service_, m_state, m_connection_type, m_state->ssl_options().support)); acceptor_.async_accept(new_connection_->socket(), boost::bind(&boosted_tcp_server<t_protocol_handler>::handle_accept, this, boost::asio::placeholders::error)); @@ -982,7 +978,7 @@ PRAGMA_WARNING_DISABLE_VS(4355) PUSH_WARNINGS DISABLE_GCC_WARNING(maybe-uninitialized) template<class t_protocol_handler> - bool boosted_tcp_server<t_protocol_handler>::init_server(const std::string port, const std::string& address, epee::net_utils::ssl_support_t ssl_support, const std::pair<std::string, std::string> &private_key_and_certificate_path, const std::list<std::string> &allowed_certificates, const std::vector<std::vector<uint8_t>> &allowed_fingerprints, bool allow_any_cert) + bool boosted_tcp_server<t_protocol_handler>::init_server(const std::string port, const std::string& address, ssl_options_t ssl_options) { uint32_t p = 0; @@ -990,7 +986,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, std::move(ssl_options)); } POP_WARNINGS //--------------------------------------------------------------------------------- @@ -1165,7 +1161,7 @@ POP_WARNINGS new_connection_->setRpcStation(); // hopefully this is not needed actually } connection_ptr conn(std::move(new_connection_)); - new_connection_.reset(new connection<t_protocol_handler>(io_service_, m_state, m_connection_type, conn->get_ssl_support(), m_ssl_context)); + new_connection_.reset(new connection<t_protocol_handler>(io_service_, m_state, m_connection_type, conn->get_ssl_support())); acceptor_.async_accept(new_connection_->socket(), boost::bind(&boosted_tcp_server<t_protocol_handler>::handle_accept, this, boost::asio::placeholders::error)); @@ -1200,7 +1196,7 @@ POP_WARNINGS assert(m_state != nullptr); // always set in constructor _erro("Some problems at accept: " << e.message() << ", connections_count = " << m_state->sock_count); misc_utils::sleep_no_w(100); - new_connection_.reset(new connection<t_protocol_handler>(io_service_, m_state, m_connection_type, new_connection_->get_ssl_support(), m_ssl_context)); + new_connection_.reset(new connection<t_protocol_handler>(io_service_, m_state, m_connection_type, new_connection_->get_ssl_support())); acceptor_.async_accept(new_connection_->socket(), boost::bind(&boosted_tcp_server<t_protocol_handler>::handle_accept, this, boost::asio::placeholders::error)); @@ -1211,7 +1207,7 @@ POP_WARNINGS { if(std::addressof(get_io_service()) == std::addressof(GET_IO_SERVICE(sock))) { - connection_ptr conn(new connection<t_protocol_handler>(std::move(sock), m_state, m_connection_type, ssl_support, m_ssl_context)); + connection_ptr conn(new connection<t_protocol_handler>(std::move(sock), m_state, m_connection_type, ssl_support)); if(conn->start(false, 1 < m_threads_count, std::move(real_remote))) { conn->get_context(out); @@ -1298,7 +1294,7 @@ POP_WARNINGS _dbg3("Connected success to " << adr << ':' << port); - const epee::net_utils::ssl_support_t ssl_support = new_connection_l->get_ssl_support(); + const ssl_support_t ssl_support = new_connection_l->get_ssl_support(); 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) { // Handshake @@ -1329,7 +1325,7 @@ POP_WARNINGS { TRY_ENTRY(); - connection_ptr new_connection_l(new connection<t_protocol_handler>(io_service_, m_state, m_connection_type, ssl_support, m_ssl_context) ); + connection_ptr new_connection_l(new connection<t_protocol_handler>(io_service_, m_state, m_connection_type, ssl_support) ); connections_mutex.lock(); connections_.insert(new_connection_l); MDEBUG("connections_ size now " << connections_.size()); @@ -1393,7 +1389,7 @@ POP_WARNINGS bool boosted_tcp_server<t_protocol_handler>::connect_async(const std::string& adr, const std::string& port, uint32_t conn_timeout, const t_callback &cb, const std::string& bind_ip, epee::net_utils::ssl_support_t ssl_support) { TRY_ENTRY(); - connection_ptr new_connection_l(new connection<t_protocol_handler>(io_service_, m_state, m_connection_type, ssl_support, m_ssl_context) ); + connection_ptr new_connection_l(new connection<t_protocol_handler>(io_service_, m_state, m_connection_type, ssl_support) ); connections_mutex.lock(); connections_.insert(new_connection_l); MDEBUG("connections_ size now " << connections_.size()); diff --git a/contrib/epee/include/net/connection_basic.hpp b/contrib/epee/include/net/connection_basic.hpp index feedc6895..d3f5f4f24 100644 --- a/contrib/epee/include/net/connection_basic.hpp +++ b/contrib/epee/include/net/connection_basic.hpp @@ -57,15 +57,30 @@ namespace epee { namespace net_utils { - struct socket_stats - { - socket_stats() - : sock_count(0), sock_number(0) - {} - - std::atomic<long> sock_count; - std::atomic<long> sock_number; - }; + + class connection_basic_shared_state + { + ssl_options_t ssl_options_; + public: + boost::asio::ssl::context ssl_context; + std::atomic<long> sock_count; + std::atomic<long> sock_number; + + connection_basic_shared_state() + : ssl_options_(ssl_support_t::e_ssl_support_disabled), + ssl_context(boost::asio::ssl::context::tlsv12), + sock_count(0), + sock_number(0) + {} + + void configure_ssl(ssl_options_t src) + { + ssl_options_ = std::move(src); + ssl_context = ssl_options_.create_context(); + } + + const ssl_options_t& ssl_options() const noexcept { return ssl_options_; } + }; /************************************************************************/ /* */ @@ -83,9 +98,10 @@ class connection_basic_pimpl; // PIMPL for this class std::string to_string(t_connection_type type); class connection_basic { // not-templated base class for rapid developmet of some code parts - // beware of removing const, net_utils::connection is sketchily doing a cast to prevent storing ptr twice - const boost::shared_ptr<socket_stats> m_stats; + // beware of removing const, net_utils::connection is sketchily doing a cast to prevent storing ptr twice + const boost::shared_ptr<connection_basic_shared_state> m_state; public: + std::unique_ptr< connection_basic_pimpl > mI; // my Implementation // moved here from orginal connecton<> - common member variables that do not depend on template in connection<> @@ -97,20 +113,19 @@ class connection_basic { // not-templated base class for rapid developmet of som /// Strand to ensure the connection's handlers are not called concurrently. boost::asio::io_service::strand strand_; /// Socket for the connection. - ssl_context_t &m_ssl_context; - ssl_support_t m_ssl_support; boost::asio::ssl::stream<boost::asio::ip::tcp::socket> socket_; + ssl_support_t m_ssl_support; public: // first counter is the ++/-- count of current sockets, the other socket_number is only-increasing ++ number generator - connection_basic(boost::asio::ip::tcp::socket&& socket, boost::shared_ptr<socket_stats> stats, ssl_support_t ssl_support, ssl_context_t &ssl_context); - connection_basic(boost::asio::io_service &io_service, boost::shared_ptr<socket_stats> stats, ssl_support_t ssl_support, ssl_context_t &ssl_context); + connection_basic(boost::asio::ip::tcp::socket&& socket, boost::shared_ptr<connection_basic_shared_state> state, ssl_support_t ssl_support); + connection_basic(boost::asio::io_service &io_service, boost::shared_ptr<connection_basic_shared_state> state, ssl_support_t ssl_support); virtual ~connection_basic() noexcept(false); - //! \return `socket_stats` object passed in construction (ptr never changes). - socket_stats& get_stats() noexcept { return *m_stats; /* verified in constructor */ } - connection_basic(boost::asio::io_service& io_service, std::atomic<long> &ref_sock_count, std::atomic<long> &sock_number, ssl_support_t ssl, ssl_context_t &ssl_context); + //! \return `shared_state` object passed in construction (ptr never changes). + connection_basic_shared_state& get_state() noexcept { return *m_state; /* verified in constructor */ } + connection_basic(boost::asio::io_service& io_service, std::atomic<long> &ref_sock_count, std::atomic<long> &sock_number, ssl_support_t ssl); boost::asio::ip::tcp::socket& socket() { return socket_.next_layer(); } ssl_support_t get_ssl_support() const { return m_ssl_support; } @@ -118,7 +133,8 @@ class connection_basic { // not-templated base class for rapid developmet of som bool handshake(boost::asio::ssl::stream_base::handshake_type type) { - return ssl_handshake(socket_, type, m_ssl_context); + //m_state != nullptr verified in constructor + return m_state->ssl_options().handshake(socket_, type); } template<typename MutableBufferSequence, typename ReadHandler> diff --git a/contrib/epee/include/net/http_client.h b/contrib/epee/include/net/http_client.h index f0425278d..a18a1d30a 100644 --- a/contrib/epee/include/net/http_client.h +++ b/contrib/epee/include/net/http_client.h @@ -275,11 +275,6 @@ namespace net_utils chunked_state m_chunked_state; std::string m_chunked_cache; critical_section m_lock; - epee::net_utils::ssl_support_t m_ssl_support; - std::pair<std::string, std::string> m_ssl_private_key_and_certificate_path; - std::list<std::string> m_ssl_allowed_certificates; - std::vector<std::vector<uint8_t>> m_ssl_allowed_fingerprints; - bool m_ssl_allow_any_cert; public: explicit http_simple_client_template() @@ -297,34 +292,28 @@ namespace net_utils , m_chunked_state() , m_chunked_cache() , m_lock() - , m_ssl_support(epee::net_utils::ssl_support_t::e_ssl_support_autodetect) {} 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<login> user, epee::net_utils::ssl_support_t ssl_support = epee::net_utils::ssl_support_t::e_ssl_support_autodetect, const std::pair<std::string, std::string> &private_key_and_certificate_path = {}, const std::list<std::string> &allowed_ssl_certificates = {}, const std::vector<std::vector<uint8_t>> &allowed_ssl_fingerprints = {}, bool allow_any_cert = false) + bool set_server(const std::string& address, boost::optional<login> user, ssl_options_t ssl_options = ssl_support_t::e_ssl_support_autodetect) { 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), std::move(ssl_options)); return true; } - void set_server(std::string host, std::string port, boost::optional<login> user, epee::net_utils::ssl_support_t ssl_support = epee::net_utils::ssl_support_t::e_ssl_support_autodetect, const std::pair<std::string, std::string> &private_key_and_certificate_path = {}, const std::list<std::string> &allowed_ssl_certificates = {}, const std::vector<std::vector<uint8_t>> &allowed_ssl_fingerprints = {}, bool allow_any_cert = false) + void set_server(std::string host, std::string port, boost::optional<login> user, ssl_options_t ssl_options = ssl_support_t::e_ssl_support_autodetect) { CRITICAL_REGION_LOCAL(m_lock); disconnect(); m_host_buff = std::move(host); m_port = std::move(port); 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_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(std::move(ssl_options)); } template<typename F> @@ -841,21 +830,21 @@ namespace net_utils const char *ptr = m_header_cache.c_str(); CHECK_AND_ASSERT_MES(!memcmp(ptr, "HTTP/", 5), false, "Invalid first response line: " + m_header_cache); ptr += 5; - CHECK_AND_ASSERT_MES(isdigit(*ptr), false, "Invalid first response line: " + m_header_cache); + CHECK_AND_ASSERT_MES(epee::misc_utils::parse::isdigit(*ptr), false, "Invalid first response line: " + m_header_cache); unsigned long ul; char *end; ul = strtoul(ptr, &end, 10); CHECK_AND_ASSERT_MES(ul <= INT_MAX && *end =='.', false, "Invalid first response line: " + m_header_cache); m_response_info.m_http_ver_hi = ul; ptr = end + 1; - CHECK_AND_ASSERT_MES(isdigit(*ptr), false, "Invalid first response line: " + m_header_cache + ", ptr: " << ptr); + CHECK_AND_ASSERT_MES(epee::misc_utils::parse::isdigit(*ptr), false, "Invalid first response line: " + m_header_cache + ", ptr: " << ptr); ul = strtoul(ptr, &end, 10); CHECK_AND_ASSERT_MES(ul <= INT_MAX && isblank(*end), false, "Invalid first response line: " + m_header_cache + ", ptr: " << ptr); m_response_info.m_http_ver_lo = ul; ptr = end + 1; while (isblank(*ptr)) ++ptr; - CHECK_AND_ASSERT_MES(isdigit(*ptr), false, "Invalid first response line: " + m_header_cache); + CHECK_AND_ASSERT_MES(epee::misc_utils::parse::isdigit(*ptr), false, "Invalid first response line: " + m_header_cache); ul = strtoul(ptr, &end, 10); CHECK_AND_ASSERT_MES(ul >= 100 && ul <= 999 && isspace(*end), false, "Invalid first response line: " + m_header_cache); m_response_info.m_response_code = ul; diff --git a/contrib/epee/include/net/http_server_impl_base.h b/contrib/epee/include/net/http_server_impl_base.h index 0922f21f2..fc2dcbf67 100644 --- a/contrib/epee/include/net/http_server_impl_base.h +++ b/contrib/epee/include/net/http_server_impl_base.h @@ -59,11 +59,7 @@ namespace epee bool init(std::function<void(size_t, uint8_t*)> rng, const std::string& bind_port = "0", const std::string& bind_ip = "0.0.0.0", std::vector<std::string> access_control_origins = std::vector<std::string>(), boost::optional<net_utils::http::login> user = boost::none, - epee::net_utils::ssl_support_t ssl_support = epee::net_utils::ssl_support_t::e_ssl_support_autodetect, - const std::pair<std::string, std::string> &private_key_and_certificate_path = {}, - std::list<std::string> allowed_certificates = {}, - std::vector<std::vector<uint8_t>> allowed_fingerprints = {}, - bool allow_any_cert = false) + net_utils::ssl_options_t ssl_options = net_utils::ssl_support_t::e_ssl_support_autodetect) { //set self as callback handler @@ -80,7 +76,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, std::move(ssl_options)); 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..66a307c97 100644 --- a/contrib/epee/include/net/net_helper.h +++ b/contrib/epee/include/net/net_helper.h @@ -101,10 +101,10 @@ namespace net_utils inline blocked_mode_client() : m_io_service(), - m_ctx({boost::asio::ssl::context(boost::asio::ssl::context::tlsv12), {}}), + m_ctx(boost::asio::ssl::context::tlsv12), m_connector(direct_connect{}), - m_ssl_socket(new boost::asio::ssl::stream<boost::asio::ip::tcp::socket>(m_io_service, m_ctx.context)), - m_ssl_support(epee::net_utils::ssl_support_t::e_ssl_support_autodetect), + m_ssl_socket(new boost::asio::ssl::stream<boost::asio::ip::tcp::socket>(m_io_service, m_ctx)), + m_ssl_options(epee::net_utils::ssl_support_t::e_ssl_support_autodetect), m_initialized(true), m_connected(false), m_deadline(m_io_service), @@ -136,13 +136,13 @@ 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<std::string, std::string> &private_key_and_certificate_path = {}, std::list<std::string> allowed_certificates = {}, std::vector<std::vector<uint8_t>> allowed_fingerprints = {}, bool allow_any_cert = false) + inline void set_ssl(ssl_options_t ssl_options) { - if (ssl_support == epee::net_utils::ssl_support_t::e_ssl_support_disabled) - m_ctx = {boost::asio::ssl::context(boost::asio::ssl::context::tlsv12), {}, {}}; + if (ssl_options) + m_ctx = ssl_options.create_context(); else - m_ctx = create_ssl_context(private_key_and_certificate_path, std::move(allowed_certificates), std::move(allowed_fingerprints), allow_any_cert); - m_ssl_support = ssl_support; + m_ctx = boost::asio::ssl::context(boost::asio::ssl::context::tlsv12); + m_ssl_options = std::move(ssl_options); } inline @@ -174,7 +174,7 @@ namespace net_utils // 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_handshake(*m_ssl_socket, boost::asio::ssl::stream_base::client, m_ctx)) + 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) { @@ -191,7 +191,7 @@ namespace net_utils return CONNECT_FAILURE; } } - m_ssl_support = epee::net_utils::ssl_support_t::e_ssl_support_enabled; + m_ssl_options.support = ssl_support_t::e_ssl_support_enabled; } return CONNECT_SUCCESS; }else @@ -212,23 +212,21 @@ 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<boost::asio::ip::tcp::socket>(m_io_service, m_ctx.context)); + m_ssl_socket.reset(new boost::asio::ssl::stream<boost::asio::ip::tcp::socket>(m_io_service, m_ctx)); // Get a list of endpoints corresponding to the server name. - try_connect_result_t try_connect_result = try_connect(addr, port, timeout, m_ssl_support); + try_connect_result_t try_connect_result = try_connect(addr, port, timeout, m_ssl_options.support); if (try_connect_result == CONNECT_FAILURE) return false; - if (m_ssl_support == epee::net_utils::ssl_support_t::e_ssl_support_autodetect) + if (m_ssl_options.support == epee::net_utils::ssl_support_t::e_ssl_support_autodetect) { - m_ssl_support = epee::net_utils::ssl_support_t::e_ssl_support_enabled; + m_ssl_options.support = epee::net_utils::ssl_support_t::e_ssl_support_enabled; if (try_connect_result == CONNECT_NO_SSL) { MERROR("SSL handshake failed on an autodetect connection, reconnecting without SSL"); - m_ssl_support = epee::net_utils::ssl_support_t::e_ssl_support_disabled; - if (try_connect(addr, port, timeout, m_ssl_support) != CONNECT_SUCCESS) + m_ssl_options.support = epee::net_utils::ssl_support_t::e_ssl_support_disabled; + if (try_connect(addr, port, timeout, m_ssl_options.support) != CONNECT_SUCCESS) return false; } } @@ -260,7 +258,7 @@ namespace net_utils if(m_connected) { m_connected = false; - if(m_ssl_support != epee::net_utils::ssl_support_t::e_ssl_support_disabled) + if(m_ssl_options) shutdown_ssl(); m_ssl_socket->next_layer().shutdown(boost::asio::ip::tcp::socket::shutdown_both); } @@ -394,7 +392,7 @@ namespace net_utils if (!m_connected || !m_ssl_socket->next_layer().is_open()) return false; if (ssl) - *ssl = m_ssl_support == epee::net_utils::ssl_support_t::e_ssl_support_enabled; + *ssl = m_ssl_options.support == ssl_support_t::e_ssl_support_enabled; return true; } @@ -558,7 +556,7 @@ namespace net_utils { m_deadline.cancel(); boost::system::error_code ec; - if(m_ssl_support != epee::net_utils::ssl_support_t::e_ssl_support_disabled) + if(m_ssl_options.support != ssl_support_t::e_ssl_support_disabled) shutdown_ssl(); m_ssl_socket->next_layer().cancel(ec); if(ec) @@ -635,7 +633,7 @@ namespace net_utils bool write(const void* data, size_t sz, boost::system::error_code& ec) { bool success; - if(m_ssl_support == epee::net_utils::ssl_support_t::e_ssl_support_enabled) + if(m_ssl_options.support == ssl_support_t::e_ssl_support_enabled) success = boost::asio::write(*m_ssl_socket, boost::asio::buffer(data, sz), ec); else success = boost::asio::write(m_ssl_socket->next_layer(), boost::asio::buffer(data, sz), ec); @@ -644,7 +642,7 @@ namespace net_utils void async_write(const void* data, size_t sz, boost::system::error_code& ec) { - if(m_ssl_support == epee::net_utils::ssl_support_t::e_ssl_support_enabled) + if(m_ssl_options.support == ssl_support_t::e_ssl_support_enabled) boost::asio::async_write(*m_ssl_socket, boost::asio::buffer(data, sz), boost::lambda::var(ec) = boost::lambda::_1); else boost::asio::async_write(m_ssl_socket->next_layer(), boost::asio::buffer(data, sz), boost::lambda::var(ec) = boost::lambda::_1); @@ -652,7 +650,7 @@ namespace net_utils void async_read(char* buff, size_t sz, boost::asio::detail::transfer_at_least_t transfer_at_least, handler_obj& hndlr) { - if(m_ssl_support != epee::net_utils::ssl_support_t::e_ssl_support_enabled) + if(m_ssl_options.support != ssl_support_t::e_ssl_support_enabled) boost::asio::async_read(m_ssl_socket->next_layer(), boost::asio::buffer(buff, sz), transfer_at_least, hndlr); else boost::asio::async_read(*m_ssl_socket, boost::asio::buffer(buff, sz), transfer_at_least, hndlr); @@ -661,10 +659,10 @@ namespace net_utils protected: boost::asio::io_service m_io_service; - epee::net_utils::ssl_context_t m_ctx; + boost::asio::ssl::context m_ctx; std::shared_ptr<boost::asio::ssl::stream<boost::asio::ip::tcp::socket>> m_ssl_socket; std::function<connect_func> m_connector; - epee::net_utils::ssl_support_t m_ssl_support; + ssl_options_t m_ssl_options; bool m_initialized; bool m_connected; boost::asio::steady_timer m_deadline; diff --git a/contrib/epee/include/net/net_ssl.h b/contrib/epee/include/net/net_ssl.h index f7b102164..957903ff8 100644 --- a/contrib/epee/include/net/net_ssl.h +++ b/contrib/epee/include/net/net_ssl.h @@ -31,10 +31,11 @@ #include <stdint.h> #include <string> -#include <list> +#include <vector> #include <boost/utility/string_ref.hpp> #include <boost/asio/ip/tcp.hpp> #include <boost/asio/ssl.hpp> +#include <boost/system/error_code.hpp> namespace epee { @@ -45,23 +46,93 @@ namespace net_utils e_ssl_support_enabled, e_ssl_support_autodetect, }; - - struct ssl_context_t - { - boost::asio::ssl::context context; - std::list<std::string> allowed_certificates; - std::vector<std::vector<uint8_t>> allowed_fingerprints; - bool allow_any_cert; - }; + + enum class ssl_verification_t : uint8_t + { + none = 0, //!< Do not verify peer. + system_ca, //!< Verify peer via system ca only (do not inspect user certificates) + user_certificates,//!< Verify peer via specific (non-chain) certificate(s) only. + user_ca //!< Verify peer via specific (possibly chain) certificate(s) only. + }; + + struct ssl_authentication_t + { + std::string private_key_path; //!< Private key used for authentication + std::string certificate_path; //!< Certificate used for authentication to peer. + + //! Load `private_key_path` and `certificate_path` into `ssl_context`. + void use_ssl_certificate(boost::asio::ssl::context &ssl_context) const; + }; + + /*! + \note `verification != disabled && support == disabled` is currently + "allowed" via public interface but obviously invalid configuation. + */ + class ssl_options_t + { + // force sorted behavior in private + std::vector<std::vector<std::uint8_t>> fingerprints_; + + public: + std::string ca_path; + ssl_authentication_t auth; + ssl_support_t support; + ssl_verification_t verification; + + //! Verification is set to system ca unless SSL is disabled. + ssl_options_t(ssl_support_t support) + : fingerprints_(), + ca_path(), + auth(), + support(support), + verification(support == ssl_support_t::e_ssl_support_disabled ? ssl_verification_t::none : ssl_verification_t::system_ca) + {} + + //! Provide user fingerprints and/or ca path. Enables SSL and user_certificate verification + ssl_options_t(std::vector<std::vector<std::uint8_t>> fingerprints, std::string ca_path); + + ssl_options_t(const ssl_options_t&) = default; + ssl_options_t(ssl_options_t&&) = default; + + ssl_options_t& operator=(const ssl_options_t&) = default; + ssl_options_t& operator=(ssl_options_t&&) = default; + + //! \return False iff ssl is disabled, otherwise true. + 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`. + bool has_fingerprint(boost::asio::ssl::verify_context &ctx) const; + + boost::asio::ssl::context create_context() const; + + /*! \note If `this->support == autodetect && this->verification != none`, + then the handshake will not fail when peer verification fails. The + assumption is that a re-connect will be attempted, so a warning is + 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 + settings. */ + 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 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<std::string, std::string> &private_key_and_certificate_path, std::list<std::string> allowed_certificates, std::vector<std::vector<uint8_t>> allowed_fingerprints, bool allow_any_cert); - void use_ssl_certificate(ssl_context_t &ssl_context, const std::pair<std::string, std::string> &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<boost::asio::ip::tcp::socket> &socket, boost::asio::ssl::stream_base::handshake_type type, const epee::net_utils::ssl_context_t &ssl_context); - bool ssl_support_from_string(ssl_support_t &ssl, boost::string_ref s); + bool ssl_support_from_string(ssl_support_t &ssl, boost::string_ref s); } } diff --git a/contrib/epee/include/span.h b/contrib/epee/include/span.h index 19720ea8b..e100452ca 100644 --- a/contrib/epee/include/span.h +++ b/contrib/epee/include/span.h @@ -31,6 +31,7 @@ #include <algorithm> #include <cstdint> #include <memory> +#include <string> #include <type_traits> namespace epee diff --git a/contrib/epee/include/storages/parserse_base_utils.h b/contrib/epee/include/storages/parserse_base_utils.h index 69b650cd4..b5c4138c5 100644 --- a/contrib/epee/include/storages/parserse_base_utils.h +++ b/contrib/epee/include/storages/parserse_base_utils.h @@ -42,13 +42,14 @@ namespace misc_utils // 4: alpha // 8: whitespace // 16: allowed in float but doesn't necessarily mean it's a float + // 32: \ and " (end of verbatim string) static const constexpr uint8_t lut[256]={ 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 0, 0, // 16 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 32 - 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 16, 18, 0, // 48 + 8, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 16, 18, 0, // 48 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 0, 0, 0, 0, 0, 0, // 64 0, 4, 4, 4, 4, 22, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, // 80 - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, // 96 + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 32, 0, 0, 0, // 96 0, 4, 4, 4, 4, 22, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, // 112 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, // 128 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -130,7 +131,7 @@ namespace misc_utils std::string::const_iterator it = star_end_string; ++it; std::string::const_iterator fi = it; - while (fi != buf_end && *fi != '\\' && *fi != '\"') + while (fi != buf_end && ((lut[(uint8_t)*fi] & 32)) == 0) ++fi; val.assign(it, fi); val.reserve(std::distance(star_end_string, buf_end)); diff --git a/contrib/epee/include/storages/portable_storage_val_converters.h b/contrib/epee/include/storages/portable_storage_val_converters.h index 36bb28627..e54cda828 100644 --- a/contrib/epee/include/storages/portable_storage_val_converters.h +++ b/contrib/epee/include/storages/portable_storage_val_converters.h @@ -144,7 +144,7 @@ POP_WARNINGS { MTRACE("Converting std::string to uint64_t. Source: " << from); // String only contains digits - if(std::all_of(from.begin(), from.end(), ::isdigit)) + if(std::all_of(from.begin(), from.end(), epee::misc_utils::parse::isdigit)) to = boost::lexical_cast<uint64_t>(from); // MyMonero ISO 8061 timestamp (2017-05-06T16:27:06Z) else if (boost::regex_match (from, boost::regex("\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d:[0-5]\\dZ"))) diff --git a/contrib/epee/include/string_tools.h b/contrib/epee/include/string_tools.h index 2e65876e6..da47b7d55 100644 --- a/contrib/epee/include/string_tools.h +++ b/contrib/epee/include/string_tools.h @@ -42,6 +42,8 @@ #include <type_traits> #include <boost/lexical_cast.hpp> #include <boost/algorithm/string/predicate.hpp> +#include "misc_log_ex.h" +#include "storages/parserse_base_utils.h" #include "hex.h" #include "memwipe.h" #include "mlocker.h" @@ -126,7 +128,7 @@ DISABLE_GCC_WARNING(maybe-uninitialized) { for (char c : str_id) { - if (!std::isdigit(c)) + if (!epee::misc_utils::parse::isdigit(c)) return false; } } diff --git a/contrib/epee/src/connection_basic.cpp b/contrib/epee/src/connection_basic.cpp index cafc08ead..19f2c7b02 100644 --- a/contrib/epee/src/connection_basic.cpp +++ b/contrib/epee/src/connection_basic.cpp @@ -65,6 +65,15 @@ namespace epee namespace net_utils { +namespace +{ + boost::asio::ssl::context& get_context(connection_basic_shared_state* state) + { + CHECK_AND_ASSERT_THROW_MES(state != nullptr, "state shared_ptr cannot be null"); + return state->ssl_context; + } +} + std::string to_string(t_connection_type type) { if (type == e_connection_type_NET) @@ -119,56 +128,54 @@ connection_basic_pimpl::connection_basic_pimpl(const std::string &name) : m_thro int connection_basic_pimpl::m_default_tos; // methods: -connection_basic::connection_basic(boost::asio::ip::tcp::socket&& sock, boost::shared_ptr<socket_stats> stats, ssl_support_t ssl_support, ssl_context_t &ssl_context) +connection_basic::connection_basic(boost::asio::ip::tcp::socket&& sock, boost::shared_ptr<connection_basic_shared_state> state, ssl_support_t ssl_support) : - m_stats(std::move(stats)), + m_state(std::move(state)), mI( new connection_basic_pimpl("peer") ), strand_(GET_IO_SERVICE(sock)), - socket_(GET_IO_SERVICE(sock), ssl_context.context), + socket_(GET_IO_SERVICE(sock), get_context(m_state.get())), m_want_close_connection(false), m_was_shutdown(false), - m_ssl_support(ssl_support), - m_ssl_context(ssl_context) + m_ssl_support(ssl_support) { // add nullptr checks if removed - CHECK_AND_ASSERT_THROW_MES(bool(m_stats), "stats shared_ptr cannot be null"); + assert(m_state != nullptr); // release runtime check in get_context socket_.next_layer() = std::move(sock); - ++(m_stats->sock_count); // increase the global counter - mI->m_peer_number = m_stats->sock_number.fetch_add(1); // use, and increase the generated number + ++(m_state->sock_count); // increase the global counter + mI->m_peer_number = m_state->sock_number.fetch_add(1); // use, and increase the generated number std::string remote_addr_str = "?"; try { boost::system::error_code e; remote_addr_str = socket().remote_endpoint(e).address().to_string(); } catch(...){} ; - _note("Spawned connection #"<<mI->m_peer_number<<" to " << remote_addr_str << " currently we have sockets count:" << m_stats->sock_count); + _note("Spawned connection #"<<mI->m_peer_number<<" to " << remote_addr_str << " currently we have sockets count:" << m_state->sock_count); } -connection_basic::connection_basic(boost::asio::io_service &io_service, boost::shared_ptr<socket_stats> stats, ssl_support_t ssl_support, ssl_context_t &ssl_context) +connection_basic::connection_basic(boost::asio::io_service &io_service, boost::shared_ptr<connection_basic_shared_state> state, ssl_support_t ssl_support) : - m_stats(std::move(stats)), + m_state(std::move(state)), mI( new connection_basic_pimpl("peer") ), strand_(io_service), - socket_(io_service, ssl_context.context), - m_want_close_connection(false), + socket_(io_service, get_context(m_state.get())), + m_want_close_connection(false), m_was_shutdown(false), - m_ssl_support(ssl_support), - m_ssl_context(ssl_context) + m_ssl_support(ssl_support) { // add nullptr checks if removed - CHECK_AND_ASSERT_THROW_MES(bool(m_stats), "stats shared_ptr cannot be null"); + assert(m_state != nullptr); // release runtime check in get_context - ++(m_stats->sock_count); // increase the global counter - mI->m_peer_number = m_stats->sock_number.fetch_add(1); // use, and increase the generated number + ++(m_state->sock_count); // increase the global counter + mI->m_peer_number = m_state->sock_number.fetch_add(1); // use, and increase the generated number std::string remote_addr_str = "?"; try { boost::system::error_code e; remote_addr_str = socket().remote_endpoint(e).address().to_string(); } catch(...){} ; - _note("Spawned connection #"<<mI->m_peer_number<<" to " << remote_addr_str << " currently we have sockets count:" << m_stats->sock_count); + _note("Spawned connection #"<<mI->m_peer_number<<" to " << remote_addr_str << " currently we have sockets count:" << m_state->sock_count); } connection_basic::~connection_basic() noexcept(false) { - --(m_stats->sock_count); + --(m_state->sock_count); std::string remote_addr_str = "?"; try { boost::system::error_code e; remote_addr_str = socket().remote_endpoint(e).address().to_string(); } catch(...){} ; diff --git a/contrib/epee/src/net_ssl.cpp b/contrib/epee/src/net_ssl.cpp index eb0b0ad65..7bedb18ac 100644 --- a/contrib/epee/src/net_ssl.cpp +++ b/contrib/epee/src/net_ssl.cpp @@ -46,8 +46,7 @@ namespace { void operator()(BIO* ptr) const noexcept { - if (ptr) - BIO_free(ptr); + BIO_free(ptr); } }; using openssl_bio = std::unique_ptr<BIO, openssl_bio_free>; @@ -56,12 +55,43 @@ namespace { void operator()(EVP_PKEY* ptr) const noexcept { - if (ptr) - EVP_PKEY_free(ptr); + EVP_PKEY_free(ptr); } }; using openssl_pkey = std::unique_ptr<EVP_PKEY, openssl_pkey_free>; + struct openssl_rsa_free + { + void operator()(RSA* ptr) const noexcept + { + RSA_free(ptr); + } + }; + using openssl_rsa = std::unique_ptr<RSA, openssl_rsa_free>; + + struct openssl_bignum_free + { + void operator()(BIGNUM* ptr) const noexcept + { + BN_free(ptr); + } + }; + using openssl_bignum = std::unique_ptr<BIGNUM, openssl_bignum_free>; + + 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 @@ -69,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) { @@ -81,19 +112,37 @@ bool create_ssl_certificate(EVP_PKEY *&pkey, X509 *&cert) } openssl_pkey pkey_deleter{pkey}; - RSA *rsa = RSA_generate_key(4096, RSA_F4, NULL, NULL); + openssl_rsa rsa{RSA_new()}; if (!rsa) { + MERROR("Error allocating RSA private key"); + return false; + } + + openssl_bignum exponent{BN_new()}; + if (!exponent) + { + MERROR("Error allocating exponent"); + return false; + } + + BN_set_word(exponent.get(), RSA_F4); + + if (RSA_generate_key_ex(rsa.get(), 4096, exponent.get(), nullptr) != 1) + { MERROR("Error generating RSA private key"); return false; } - if (EVP_PKEY_assign_RSA(pkey, rsa) <= 0) // The RSA will be automatically freed when the EVP_PKEY structure is freed. + + if (EVP_PKEY_assign_RSA(pkey, rsa.get()) <= 0) { MERROR("Error assigning RSA private key"); - RSA_free(rsa); return false; } + // the RSA key is now managed by the EVP_PKEY structure + (void)rsa.release(); + cert = X509_new(); if (!cert) { @@ -122,22 +171,34 @@ bool create_ssl_certificate(EVP_PKEY *&pkey, X509 *&cert) return true; } -ssl_context_t create_ssl_context(const std::pair<std::string, std::string> &private_key_and_certificate_path, std::list<std::string> allowed_certificates, std::vector<std::vector<uint8_t>> allowed_fingerprints, bool allow_any_cert) +ssl_options_t::ssl_options_t(std::vector<std::vector<std::uint8_t>> fingerprints, std::string ca_path) + : fingerprints_(std::move(fingerprints)), + ca_path(std::move(ca_path)), + auth(), + support(ssl_support_t::e_ssl_support_enabled), + verification(ssl_verification_t::user_certificates) { - ssl_context_t ssl_context{boost::asio::ssl::context(boost::asio::ssl::context::tlsv12), std::move(allowed_certificates), std::move(allowed_fingerprints)}; + std::sort(fingerprints_.begin(), fingerprints_.end()); +} + +boost::asio::ssl::context ssl_options_t::create_context() const +{ + boost::asio::ssl::context ssl_context{boost::asio::ssl::context::tlsv12}; + if (!bool(*this)) + return ssl_context; // only allow tls v1.2 and up - ssl_context.context.set_options(boost::asio::ssl::context::default_workarounds); - ssl_context.context.set_options(boost::asio::ssl::context::no_sslv2); - ssl_context.context.set_options(boost::asio::ssl::context::no_sslv3); - ssl_context.context.set_options(boost::asio::ssl::context::no_tlsv1); - ssl_context.context.set_options(boost::asio::ssl::context::no_tlsv1_1); + ssl_context.set_options(boost::asio::ssl::context::default_workarounds); + ssl_context.set_options(boost::asio::ssl::context::no_sslv2); + ssl_context.set_options(boost::asio::ssl::context::no_sslv3); + ssl_context.set_options(boost::asio::ssl::context::no_tlsv1); + ssl_context.set_options(boost::asio::ssl::context::no_tlsv1_1); // only allow a select handful of tls v1.3 and v1.2 ciphers to be used - SSL_CTX_set_cipher_list(ssl_context.context.native_handle(), "ECDHE-ECDSA-CHACHA20-POLY1305-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-CHACHA20-POLY1305"); + SSL_CTX_set_cipher_list(ssl_context.native_handle(), "ECDHE-ECDSA-CHACHA20-POLY1305-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-CHACHA20-POLY1305"); // set options on the SSL context for added security - SSL_CTX *ctx = ssl_context.context.native_handle(); + SSL_CTX *ctx = ssl_context.native_handle(); CHECK_AND_ASSERT_THROW_MES(ctx, "Failed to get SSL context"); SSL_CTX_clear_options(ctx, SSL_OP_LEGACY_SERVER_CONNECT); // SSL_CTX_SET_OPTIONS(3) SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF); // https://stackoverflow.com/questions/22378442 @@ -153,10 +214,29 @@ ssl_context_t create_ssl_context(const std::pair<std::string, std::string> &priv #ifdef SSL_OP_NO_COMPRESSION SSL_CTX_set_options(ctx, SSL_OP_NO_COMPRESSION); #endif - ssl_context.context.set_default_verify_paths(); - 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()) + switch (verification) + { + case ssl_verification_t::system_ca: + ssl_context.set_default_verify_paths(); + break; + case ssl_verification_t::user_certificates: + ssl_context.set_verify_depth(0); + /* fallthrough */ + case ssl_verification_t::user_ca: + if (!ca_path.empty()) + { + const boost::system::error_code err = load_ca_file(ssl_context, ca_path); + if (err) + throw boost::system::system_error{err, "Failed to load user CA file at " + ca_path}; + } + break; + default: + break; + } + + CHECK_AND_ASSERT_THROW_MES(auth.private_key_path.empty() == auth.certificate_path.empty(), "private key and certificate must be either both given or both empty"); + if (auth.private_key_path.empty()) { EVP_PKEY *pkey; X509 *cert; @@ -167,19 +247,15 @@ ssl_context_t create_ssl_context(const std::pair<std::string, std::string> &priv EVP_PKEY_free(pkey); } else - { - ssl_context.context.use_private_key_file(private_key_and_certificate_path.first, boost::asio::ssl::context::pem); - ssl_context.context.use_certificate_file(private_key_and_certificate_path.second, boost::asio::ssl::context::pem); - } - ssl_context.allow_any_cert = allow_any_cert; + auth.use_ssl_certificate(ssl_context); return ssl_context; } -void use_ssl_certificate(ssl_context_t &ssl_context, const std::pair<std::string, std::string> &private_key_and_certificate_path) +void ssl_authentication_t::use_ssl_certificate(boost::asio::ssl::context &ssl_context) const { - ssl_context.context.use_private_key_file(private_key_and_certificate_path.first, boost::asio::ssl::context::pem); - ssl_context.context.use_certificate_file(private_key_and_certificate_path.second, boost::asio::ssl::context::pem); + ssl_context.use_private_key_file(private_key_path, boost::asio::ssl::context::pem); + ssl_context.use_certificate_chain_file(certificate_path); } bool is_ssl(const unsigned char *data, size_t len) @@ -202,23 +278,44 @@ bool is_ssl(const unsigned char *data, size_t len) return false; } -bool is_certificate_allowed(boost::asio::ssl::verify_context &ctx, const ssl_context_t &ssl_context) +bool ssl_options_t::has_strong_verification(boost::string_ref host) const noexcept { - 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) + // 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) { - MERROR("No certificate found in verify_context"); - return false; + 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 +{ // can we check the certificate against a list of fingerprints? - if (!ssl_context.allowed_fingerprints.empty()) { + if (!fingerprints_.empty()) { + X509_STORE_CTX *sctx = ctx.native_handle(); + if (!sctx) + { + MERROR("Error getting verify_context handle"); + return false; + } + + X509* cert = nullptr; + const STACK_OF(X509)* chain = X509_STORE_CTX_get_chain(sctx); + if (!chain || sk_X509_num(chain) < 1 || !(cert = sk_X509_value(chain, 0))) + { + MERROR("No certificate found in verify_context"); + return false; + } + // buffer for the certificate digest and the size of the result std::vector<uint8_t> digest(EVP_MAX_MD_SIZE); unsigned int size{ 0 }; @@ -232,74 +329,63 @@ bool is_certificate_allowed(boost::asio::ssl::verify_context &ctx, const ssl_con // strip unnecessary bytes from the digest digest.resize(size); - // is the certificate fingerprint inside the list of allowed fingerprints? - if (std::find(ssl_context.allowed_fingerprints.begin(), ssl_context.allowed_fingerprints.end(), digest) != ssl_context.allowed_fingerprints.end()) - 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; + return std::binary_search(fingerprints_.begin(), fingerprints_.end(), digest); } - // if either checklist is non-empty we must have failed it - return ssl_context.allowed_fingerprints.empty() && ssl_context.allowed_certificates.empty(); + return false; } -bool ssl_handshake(boost::asio::ssl::stream<boost::asio::ip::tcp::socket> &socket, boost::asio::ssl::stream_base::handshake_type type, const epee::net_utils::ssl_context_t &ssl_context) +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 { - 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) + + /* Using system-wide CA store for client verification is funky - there is + no expected hostname for server to verify against. If server doesn't have + specific whitelisted certificates for client, don't require client to + send certificate at all. */ + const bool no_verification = verification == ssl_verification_t::none || + (type == boost::asio::ssl::stream_base::server && fingerprints_.empty() && ca_path.empty()); + + /* According to OpenSSL documentation (and SSL specifications), server must + always send certificate unless "anonymous" cipher mode is used which are + disabled by default. Either way, the certificate is never inspected. */ + if (no_verification) + socket.set_verify_mode(boost::asio::ssl::verify_none); + else { - if (!preverified) + socket.set_verify_mode(boost::asio::ssl::verify_peer | boost::asio::ssl::verify_fail_if_no_peer_cert); + + // 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) { - 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) + // preverified means it passed system or user CA check. System CA is never loaded + // when fingerprints are whitelisted. + 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)) { - MERROR("Invalid SSL certificate, error " << err << " at depth " << depth << ", connection dropped"); - return false; + // autodetect will reconnect without SSL - warn and keep connection encrypted + if (support != ssl_support_t::e_ssl_support_autodetect) + { + MERROR("SSL certificate is not in the allowed list, connection droppped"); + return false; + } + MWARNING("SSL peer has not been verified"); } - } - if (!ssl_context.allow_any_cert && !is_certificate_allowed(ctx, ssl_context)) - { - MERROR("Certificate is not in the allowed list, connection droppped"); - return false; - } - verified = true; - return true; - }); + return true; + }); + } boost::system::error_code ec; socket.handshake(type, ec); if (ec) { - MERROR("handshake failed, connection dropped: " << ec.message()); - return false; - } - if (!ssl_context.allow_any_cert && !verified) - { - MERROR("Peer did not provide a certificate in the allowed list, connection dropped"); + MERROR("SSL handshake failed, connection dropped: " << ec.message()); return false; } MDEBUG("SSL handshake success"); diff --git a/src/common/perf_timer.cpp b/src/common/perf_timer.cpp index dda498088..189eb85eb 100644 --- a/src/common/perf_timer.cpp +++ b/src/common/perf_timer.cpp @@ -88,7 +88,7 @@ namespace tools namespace tools { -el::Level performance_timer_log_level = el::Level::Debug; +el::Level performance_timer_log_level = el::Level::Info; static __thread std::vector<LoggingPerformanceTimer*> *performance_timers = NULL; @@ -97,8 +97,8 @@ void set_performance_timer_log_level(el::Level level) if (level != el::Level::Debug && level != el::Level::Trace && level != el::Level::Info && level != el::Level::Warning && level != el::Level::Error && level != el::Level::Fatal) { - MERROR("Wrong log level: " << el::LevelHelper::convertToString(level) << ", using Debug"); - level = el::Level::Debug; + MERROR("Wrong log level: " << el::LevelHelper::convertToString(level) << ", using Info"); + level = el::Level::Info; } performance_timer_log_level = level; } diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp index 094057b1f..fecd67729 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.cpp +++ b/src/cryptonote_basic/cryptonote_format_utils.cpp @@ -1090,7 +1090,14 @@ namespace cryptonote // we still need the size if (blob_size) - *blob_size = get_object_blobsize(t); + { + if (!t.is_blob_size_valid()) + { + t.blob_size = blob.size(); + t.set_blob_size_valid(true); + } + *blob_size = t.blob_size; + } return true; } @@ -1143,21 +1150,37 @@ namespace cryptonote return blob; } //--------------------------------------------------------------- - bool calculate_block_hash(const block& b, crypto::hash& res) + bool calculate_block_hash(const block& b, crypto::hash& res, const blobdata *blob) { + blobdata bd; + if (!blob) + { + bd = block_to_blob(b); + blob = &bd; + } + + bool hash_result = get_object_hash(get_block_hashing_blob(b), res); + if (!hash_result) + return false; + + if (b.miner_tx.vin.size() == 1 && b.miner_tx.vin[0].type() == typeid(cryptonote::txin_gen)) + { + const cryptonote::txin_gen &txin_gen = boost::get<cryptonote::txin_gen>(b.miner_tx.vin[0]); + if (txin_gen.height != 202612) + return true; + } + // EXCEPTION FOR BLOCK 202612 const std::string correct_blob_hash_202612 = "3a8a2b3a29b50fc86ff73dd087ea43c6f0d6b8f936c849194d5c84c737903966"; const std::string existing_block_id_202612 = "bbd604d2ba11ba27935e006ed39c9bfdd99b76bf4a50654bc1e1e61217962698"; - crypto::hash block_blob_hash = get_blob_hash(block_to_blob(b)); + crypto::hash block_blob_hash = get_blob_hash(*blob); if (string_tools::pod_to_hex(block_blob_hash) == correct_blob_hash_202612) { string_tools::hex_to_pod(existing_block_id_202612, res); return true; } - bool hash_result = get_object_hash(get_block_hashing_blob(b), res); - if (hash_result) { // make sure that we aren't looking at a block with the 202612 block id but not the correct blobdata if (string_tools::pod_to_hex(res) == existing_block_id_202612) @@ -1200,9 +1223,9 @@ namespace cryptonote bool get_block_longhash(const block& b, crypto::hash& res, uint64_t height) { // block 202612 bug workaround - const std::string longhash_202612 = "84f64766475d51837ac9efbef1926486e58563c95a19fef4aec3254f03000000"; if (height == 202612) { + static const std::string longhash_202612 = "84f64766475d51837ac9efbef1926486e58563c95a19fef4aec3254f03000000"; string_tools::hex_to_pod(longhash_202612, res); return true; } @@ -1239,7 +1262,7 @@ namespace cryptonote return p; } //--------------------------------------------------------------- - bool parse_and_validate_block_from_blob(const blobdata& b_blob, block& b) + bool parse_and_validate_block_from_blob(const blobdata& b_blob, block& b, crypto::hash *block_hash) { std::stringstream ss; ss << b_blob; @@ -1248,9 +1271,26 @@ namespace cryptonote CHECK_AND_ASSERT_MES(r, false, "Failed to parse block from blob"); b.invalidate_hashes(); b.miner_tx.invalidate_hashes(); + if (block_hash) + { + calculate_block_hash(b, *block_hash, &b_blob); + ++block_hashes_calculated_count; + b.hash = *block_hash; + b.set_hash_valid(true); + } return true; } //--------------------------------------------------------------- + bool parse_and_validate_block_from_blob(const blobdata& b_blob, block& b) + { + return parse_and_validate_block_from_blob(b_blob, b, NULL); + } + //--------------------------------------------------------------- + bool parse_and_validate_block_from_blob(const blobdata& b_blob, block& b, crypto::hash &block_hash) + { + return parse_and_validate_block_from_blob(b_blob, b, &block_hash); + } + //--------------------------------------------------------------- blobdata block_to_blob(const block& b) { return t_serializable_object_to_blob(b); @@ -1286,6 +1326,7 @@ namespace cryptonote crypto::hash get_tx_tree_hash(const block& b) { std::vector<crypto::hash> txs_ids; + txs_ids.reserve(1 + b.tx_hashes.size()); crypto::hash h = null_hash; size_t bl_sz = 0; get_transaction_hash(b.miner_tx, h, bl_sz); diff --git a/src/cryptonote_basic/cryptonote_format_utils.h b/src/cryptonote_basic/cryptonote_format_utils.h index 40a9907be..3f8eef076 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.h +++ b/src/cryptonote_basic/cryptonote_format_utils.h @@ -114,12 +114,14 @@ namespace cryptonote crypto::hash get_pruned_transaction_hash(const transaction& t, const crypto::hash &pruned_data_hash); blobdata get_block_hashing_blob(const block& b); - bool calculate_block_hash(const block& b, crypto::hash& res); + bool calculate_block_hash(const block& b, crypto::hash& res, const blobdata *blob = NULL); bool get_block_hash(const block& b, crypto::hash& res); crypto::hash get_block_hash(const block& b); bool get_block_longhash(const block& b, crypto::hash& res, uint64_t height); crypto::hash get_block_longhash(const block& b, uint64_t height); + bool parse_and_validate_block_from_blob(const blobdata& b_blob, block& b, crypto::hash *block_hash); bool parse_and_validate_block_from_blob(const blobdata& b_blob, block& b); + bool parse_and_validate_block_from_blob(const blobdata& b_blob, block& b, crypto::hash &block_hash); bool get_inputs_money_amount(const transaction& tx, uint64_t& money); uint64_t get_outs_money_amount(const transaction& tx); bool check_inputs_types_supported(const transaction& tx); diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 5925548d6..503267a1a 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -3176,6 +3176,7 @@ bool Blockchain::check_fee(size_t tx_weight, uint64_t fee) const if (version >= HF_VERSION_DYNAMIC_FEE) { median = m_current_block_cumul_weight_limit / 2; + const uint64_t blockchain_height = m_db->height(); already_generated_coins = blockchain_height ? m_db->get_block_already_generated_coins(blockchain_height - 1) : 0; if (!get_block_reward(median, 1, already_generated_coins, base_reward, version)) return false; @@ -4338,8 +4339,9 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete for (unsigned int j = 0; j < batches; j++, ++blockidx) { block &block = blocks[blockidx]; + crypto::hash block_hash; - if (!parse_and_validate_block_from_blob(it->block, block)) + if (!parse_and_validate_block_from_blob(it->block, block, block_hash)) return false; // check first block and skip all blocks if its not chained properly @@ -4352,7 +4354,7 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete return true; } } - if (have_block(get_block_hash(block))) + if (have_block(block_hash)) blocks_exist = true; std::advance(it, 1); @@ -4362,11 +4364,12 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete for (unsigned i = 0; i < extra && !blocks_exist; i++, blockidx++) { block &block = blocks[blockidx]; + crypto::hash block_hash; - if (!parse_and_validate_block_from_blob(it->block, block)) + if (!parse_and_validate_block_from_blob(it->block, block, block_hash)) return false; - if (have_block(get_block_hash(block))) + if (have_block(block_hash)) blocks_exist = true; std::advance(it, 1); diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 87cfb27e1..6b0052dc0 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -1438,7 +1438,8 @@ namespace cryptonote block lb; if (!b) { - if(!parse_and_validate_block_from_blob(block_blob, lb)) + crypto::hash block_hash; + if(!parse_and_validate_block_from_blob(block_blob, lb, block_hash)) { LOG_PRINT_L1("Failed to parse and validate new block"); bvc.m_verifivation_failed = true; diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index 32f0afceb..b7a50783a 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -48,6 +48,17 @@ #define MONERO_DEFAULT_LOG_CATEGORY "net.cn" #define MLOG_P2P_MESSAGE(x) MCINFO("net.p2p.msg", context << x) +#define MLOGIF_P2P_MESSAGE(init, test, x) \ + do { \ + const auto level = el::Level::Info; \ + const char *cat = "net.p2p.msg"; \ + if (ELPP->vRegistry()->allowed(level, cat)) { \ + init; \ + if (test) \ + el::base::Writer(level, __FILE__, __LINE__, ELPP_FUNC, el::base::DispatchAction::NormalLog).construct(cat) << x; \ + } \ + } while(0) + #define MLOG_PEER_STATE(x) \ MCINFO(MONERO_DEFAULT_LOG_CATEGORY, context << "[" << epee::string_tools::to_string_hex(context.m_pruning_seed) << "] state: " << x << " in state " << cryptonote::get_protocol_state_string(context.m_state)) @@ -418,7 +429,7 @@ namespace cryptonote template<class t_core> int t_cryptonote_protocol_handler<t_core>::handle_notify_new_block(int command, NOTIFY_NEW_BLOCK::request& arg, cryptonote_connection_context& context) { - MLOG_P2P_MESSAGE("Received NOTIFY_NEW_BLOCK (" << arg.b.txs.size() << " txes)"); + MLOGIF_P2P_MESSAGE(crypto::hash hash; cryptonote::block b; bool ret = cryptonote::parse_and_validate_block_from_blob(arg.b.block, b, &hash);, ret, "Received NOTIFY_NEW_BLOCK " << hash << " (height " << arg.current_blockchain_height << ", " << arg.b.txs.size() << " txes)"); if(context.m_state != cryptonote_connection_context::state_normal) return 1; if(!is_synchronized()) // can happen if a peer connection goes to normal but another thread still hasn't finished adding queued blocks @@ -488,7 +499,7 @@ namespace cryptonote template<class t_core> int t_cryptonote_protocol_handler<t_core>::handle_notify_new_fluffy_block(int command, NOTIFY_NEW_FLUFFY_BLOCK::request& arg, cryptonote_connection_context& context) { - MLOG_P2P_MESSAGE("Received NOTIFY_NEW_FLUFFY_BLOCK (height " << arg.current_blockchain_height << ", " << arg.b.txs.size() << " txes)"); + MLOGIF_P2P_MESSAGE(crypto::hash hash; cryptonote::block b; bool ret = cryptonote::parse_and_validate_block_from_blob(arg.b.block, b, &hash);, ret, "Received NOTIFY_NEW_FLUFFY_BLOCK " << hash << " (height " << arg.current_blockchain_height << ", " << arg.b.txs.size() << " txes)"); if(context.m_state != cryptonote_connection_context::state_normal) return 1; if(!is_synchronized()) // can happen if a peer connection goes to normal but another thread still hasn't finished adding queued blocks @@ -858,6 +869,9 @@ namespace cryptonote int t_cryptonote_protocol_handler<t_core>::handle_notify_new_transactions(int command, NOTIFY_NEW_TRANSACTIONS::request& arg, cryptonote_connection_context& context) { MLOG_P2P_MESSAGE("Received NOTIFY_NEW_TRANSACTIONS (" << arg.txs.size() << " txes)"); + for (const auto &blob: arg.txs) + MLOGIF_P2P_MESSAGE(cryptonote::transaction tx; crypto::hash hash; bool ret = cryptonote::parse_and_validate_tx_from_blob(blob, tx, hash);, ret, "Including transaction " << hash); + if(context.m_state != cryptonote_connection_context::state_normal) return 1; @@ -995,7 +1009,8 @@ namespace cryptonote return 1; } - if(!parse_and_validate_block_from_blob(block_entry.block, b)) + crypto::hash block_hash; + if(!parse_and_validate_block_from_blob(block_entry.block, b, block_hash)) { LOG_ERROR_CCONTEXT("sent wrong block: failed to parse and validate block: " << epee::string_tools::buff_to_hex_nodelimer(block_entry.block) << ", dropping connection"); @@ -1014,7 +1029,6 @@ namespace cryptonote if (start_height == std::numeric_limits<uint64_t>::max()) start_height = boost::get<txin_gen>(b.miner_tx.vin[0]).height; - const crypto::hash block_hash = get_block_hash(b); auto req_it = context.m_requested_objects.find(block_hash); if(req_it == context.m_requested_objects.end()) { @@ -1121,13 +1135,13 @@ namespace cryptonote << ", we need " << previous_height); block new_block; - if (!parse_and_validate_block_from_blob(blocks.back().block, new_block)) + crypto::hash last_block_hash; + if (!parse_and_validate_block_from_blob(blocks.back().block, new_block, last_block_hash)) { MERROR(context << "Failed to parse block, but it should already have been parsed"); m_block_queue.remove_spans(span_connection_id, start_height); continue; } - const crypto::hash last_block_hash = cryptonote::get_block_hash(new_block); if (m_core.have_block(last_block_hash)) { const uint64_t subchain_height = start_height + blocks.size(); diff --git a/src/lmdb/database.cpp b/src/lmdb/database.cpp index c6b244671..ccab1902a 100644 --- a/src/lmdb/database.cpp +++ b/src/lmdb/database.cpp @@ -46,7 +46,7 @@ namespace lmdb { namespace { - constexpr const std::size_t max_resize = 1 * 1024 * 1024 * 1024; // 1 GB + constexpr const mdb_size_t max_resize = 1 * 1024 * 1024 * 1024; // 1 GB void acquire_context(context& ctx) noexcept { while (ctx.lock.test_and_set()); @@ -136,7 +136,7 @@ namespace lmdb MDB_envinfo info{}; MONERO_LMDB_CHECK(mdb_env_info(handle(), &info)); - const std::size_t resize = std::min(info.me_mapsize, max_resize); + const mdb_size_t resize = std::min(info.me_mapsize, max_resize); const int err = mdb_env_set_mapsize(handle(), info.me_mapsize + resize); ctx.lock.clear(); if (err) diff --git a/src/lmdb/value_stream.cpp b/src/lmdb/value_stream.cpp index 1024deb06..604140e47 100644 --- a/src/lmdb/value_stream.cpp +++ b/src/lmdb/value_stream.cpp @@ -36,9 +36,9 @@ namespace lmdb { namespace stream { - std::size_t count(MDB_cursor* cur) + mdb_size_t count(MDB_cursor* cur) { - std::size_t out = 0; + mdb_size_t out = 0; if (cur) { const int rc = mdb_cursor_count(cur, &out); diff --git a/src/lmdb/value_stream.h b/src/lmdb/value_stream.h index c9977221f..01090aa67 100644 --- a/src/lmdb/value_stream.h +++ b/src/lmdb/value_stream.h @@ -43,7 +43,7 @@ namespace lmdb \throw std::system_error if unexpected LMDB error. \return 0 if `cur == nullptr`, otherwise count of values at current key. */ - std::size_t count(MDB_cursor* cur); + mdb_size_t count(MDB_cursor* cur); /*! Calls `mdb_cursor_get` and does some error checking. diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index f5f1a2f9a..161ad2951 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); @@ -149,36 +149,38 @@ namespace cryptonote if (rpc_config->login) http_login.emplace(std::move(rpc_config->login->username), std::move(rpc_config->login->password).password()); - epee::net_utils::ssl_support_t ssl_support; - const std::string ssl = command_line::get_arg(vm, arg_rpc_ssl); - if (!epee::net_utils::ssl_support_from_string(ssl_support, ssl)) + epee::net_utils::ssl_options_t ssl_options = epee::net_utils::ssl_support_t::e_ssl_support_autodetect; + if (command_line::get_arg(vm, arg_rpc_ssl_allow_any_cert)) + ssl_options.verification = epee::net_utils::ssl_verification_t::none; + else { - MFATAL("Invalid RPC SSL support: " << ssl); - return false; + std::string ssl_ca_path = command_line::get_arg(vm, arg_rpc_ssl_ca_certificates); + const std::vector<std::string> ssl_allowed_fingerprint_strings = command_line::get_arg(vm, arg_rpc_ssl_allowed_fingerprints); + std::vector<std::vector<uint8_t>> ssl_allowed_fingerprints{ ssl_allowed_fingerprint_strings.size() }; + std::transform(ssl_allowed_fingerprint_strings.begin(), ssl_allowed_fingerprint_strings.end(), ssl_allowed_fingerprints.begin(), epee::from_hex::vector); + + if (!ssl_ca_path.empty() || !ssl_allowed_fingerprints.empty()) + ssl_options = epee::net_utils::ssl_options_t{std::move(ssl_allowed_fingerprints), std::move(ssl_ca_path)}; } - 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<std::string> ssl_allowed_certificate_paths = command_line::get_arg(vm, arg_rpc_ssl_allowed_certificates); - std::list<std::string> ssl_allowed_certificates; - for (const std::string &path: ssl_allowed_certificate_paths) + + ssl_options.auth = epee::net_utils::ssl_authentication_t{ + command_line::get_arg(vm, arg_rpc_ssl_private_key), command_line::get_arg(vm, arg_rpc_ssl_certificate) + }; + + // user specified CA file or fingeprints implies enabled SSL by default + if (ssl_options.verification != epee::net_utils::ssl_verification_t::user_certificates || !command_line::is_arg_defaulted(vm, arg_rpc_ssl)) { - ssl_allowed_certificates.push_back({}); - if (!epee::file_io_utils::load_file_to_string(path, ssl_allowed_certificates.back())) + const std::string ssl = command_line::get_arg(vm, arg_rpc_ssl); + if (!epee::net_utils::ssl_support_from_string(ssl_options.support, ssl)) { - MERROR("Failed to load certificate: " << path); - ssl_allowed_certificates.back() = std::string(); + MFATAL("Invalid RPC SSL support: " << ssl); + return false; } } - const std::vector<std::string> ssl_allowed_fingerprint_strings = command_line::get_arg(vm, arg_rpc_ssl_allowed_fingerprints); - std::vector<std::vector<uint8_t>> ssl_allowed_fingerprints{ ssl_allowed_fingerprint_strings.size() }; - std::transform(ssl_allowed_fingerprint_strings.begin(), ssl_allowed_fingerprint_strings.end(), ssl_allowed_fingerprints.begin(), epee::from_hex::vector); - const bool ssl_allow_any_cert = command_line::get_arg(vm, arg_rpc_ssl_allow_any_cert); - auto rng = [](size_t len, uint8_t *ptr){ return crypto::rand(len, ptr); }; return epee::http_server_impl_base<core_rpc_server, connection_context>::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 + rng, std::move(port), std::move(rpc_config->bind_ip), std::move(rpc_config->access_control_origins), std::move(http_login), std::move(ssl_options) ); } //------------------------------------------------------------------------------------------------------------------------------ @@ -247,7 +249,6 @@ namespace cryptonote res.cumulative_difficulty, res.wide_cumulative_difficulty, res.cumulative_difficulty_top64); res.block_size_limit = res.block_weight_limit = m_core.get_blockchain_storage().get_current_cumulative_block_weight_limit(); res.block_size_median = res.block_weight_median = m_core.get_blockchain_storage().get_current_cumulative_block_weight_median(); - res.status = CORE_RPC_STATUS_OK; res.start_time = restricted ? 0 : (uint64_t)m_core.get_start_time(); res.free_space = restricted ? std::numeric_limits<uint64_t>::max() : m_core.get_free_space(); res.offline = m_core.offline(); @@ -265,6 +266,8 @@ namespace cryptonote res.database_size = round_up(res.database_size, 5ull* 1024 * 1024 * 1024); res.update_available = restricted ? false : m_core.is_update_available(); res.version = restricted ? "" : MONERO_VERSION; + + res.status = CORE_RPC_STATUS_OK; return true; } //------------------------------------------------------------------------------------------------------------------------------ @@ -646,30 +649,61 @@ namespace cryptonote e.prunable_hash = epee::string_tools::pod_to_hex(std::get<2>(tx)); if (req.split || req.prune || std::get<3>(tx).empty()) { + // use splitted form with pruned and prunable (filled only when prune=false and the daemon has it), leaving as_hex as empty e.pruned_as_hex = string_tools::buff_to_hex_nodelimer(std::get<1>(tx)); if (!req.prune) e.prunable_as_hex = string_tools::buff_to_hex_nodelimer(std::get<3>(tx)); - } - else - { - cryptonote::blobdata tx_data; - if (req.prune) - tx_data = std::get<1>(tx); - else - tx_data = std::get<1>(tx) + std::get<3>(tx); - e.as_hex = string_tools::buff_to_hex_nodelimer(tx_data); - if (req.decode_as_json && !tx_data.empty()) + if (req.decode_as_json) { + cryptonote::blobdata tx_data; cryptonote::transaction t; - if (cryptonote::parse_and_validate_tx_from_blob(tx_data, t)) + if (req.prune || std::get<3>(tx).empty()) { - if (req.prune) + // decode pruned tx to JSON + tx_data = std::get<1>(tx); + if (cryptonote::parse_and_validate_tx_base_from_blob(tx_data, t)) { pruned_transaction pruned_tx{t}; e.as_json = obj_to_json_str(pruned_tx); } else + { + res.status = "Failed to parse and validate pruned tx from blob"; + return true; + } + } + else + { + // decode full tx to JSON + tx_data = std::get<1>(tx) + std::get<3>(tx); + if (cryptonote::parse_and_validate_tx_from_blob(tx_data, t)) + { e.as_json = obj_to_json_str(t); + } + else + { + res.status = "Failed to parse and validate tx from blob"; + return true; + } + } + } + } + else + { + // use non-splitted form, leaving pruned_as_hex and prunable_as_hex as empty + cryptonote::blobdata tx_data = std::get<1>(tx) + std::get<3>(tx); + e.as_hex = string_tools::buff_to_hex_nodelimer(tx_data); + if (req.decode_as_json) + { + cryptonote::transaction t; + if (cryptonote::parse_and_validate_tx_from_blob(tx_data, t)) + { + e.as_json = obj_to_json_str(t); + } + else + { + res.status = "Failed to parse and validate tx from blob"; + return true; } } } @@ -1718,67 +1752,7 @@ namespace cryptonote //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_get_info_json(const COMMAND_RPC_GET_INFO::request& req, COMMAND_RPC_GET_INFO::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx) { - PERF_TIMER(on_get_info_json); - bool r; - if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_INFO>(invoke_http_mode::JON_RPC, "get_info", req, res, r)) - { - res.bootstrap_daemon_address = m_bootstrap_daemon_address; - crypto::hash top_hash; - m_core.get_blockchain_top(res.height_without_bootstrap, top_hash); - ++res.height_without_bootstrap; // turn top block height into blockchain height - res.was_bootstrap_ever_used = true; - return r; - } - - const bool restricted = m_restricted && ctx; - - crypto::hash top_hash; - m_core.get_blockchain_top(res.height, top_hash); - ++res.height; // turn top block height into blockchain height - res.top_block_hash = string_tools::pod_to_hex(top_hash); - res.target_height = m_core.get_target_blockchain_height(); - store_difficulty(m_core.get_blockchain_storage().get_difficulty_for_next_block(), - res.difficulty, res.wide_difficulty, res.difficulty_top64); - res.target = m_core.get_blockchain_storage().get_current_hard_fork_version() < 2 ? DIFFICULTY_TARGET_V1 : DIFFICULTY_TARGET_V2; - res.tx_count = m_core.get_blockchain_storage().get_total_transactions() - res.height; //without coinbase - res.tx_pool_size = m_core.get_pool_transactions_count(); - res.alt_blocks_count = restricted ? 0 : m_core.get_blockchain_storage().get_alternative_blocks_count(); - uint64_t total_conn = restricted ? 0 : m_p2p.get_public_connections_count(); - res.outgoing_connections_count = restricted ? 0 : m_p2p.get_public_outgoing_connections_count(); - res.incoming_connections_count = restricted ? 0 : (total_conn - res.outgoing_connections_count); - res.rpc_connections_count = restricted ? 0 : get_connections_count(); - res.white_peerlist_size = restricted ? 0 : m_p2p.get_public_white_peers_count(); - res.grey_peerlist_size = restricted ? 0 : m_p2p.get_public_gray_peers_count(); - - cryptonote::network_type net_type = nettype(); - res.mainnet = net_type == MAINNET; - res.testnet = net_type == TESTNET; - res.stagenet = net_type == STAGENET; - res.nettype = net_type == MAINNET ? "mainnet" : net_type == TESTNET ? "testnet" : net_type == STAGENET ? "stagenet" : "fakechain"; - - store_difficulty(m_core.get_blockchain_storage().get_db().get_block_cumulative_difficulty(res.height - 1), - res.cumulative_difficulty, res.wide_cumulative_difficulty, res.cumulative_difficulty_top64); - res.block_size_limit = res.block_weight_limit = m_core.get_blockchain_storage().get_current_cumulative_block_weight_limit(); - res.block_size_median = res.block_weight_median = m_core.get_blockchain_storage().get_current_cumulative_block_weight_median(); - res.status = CORE_RPC_STATUS_OK; - res.start_time = restricted ? 0 : (uint64_t)m_core.get_start_time(); - res.free_space = restricted ? std::numeric_limits<uint64_t>::max() : m_core.get_free_space(); - res.offline = m_core.offline(); - res.bootstrap_daemon_address = restricted ? "" : m_bootstrap_daemon_address; - res.height_without_bootstrap = restricted ? 0 : res.height; - if (restricted) - res.was_bootstrap_ever_used = false; - else - { - boost::shared_lock<boost::shared_mutex> lock(m_bootstrap_daemon_mutex); - res.was_bootstrap_ever_used = m_was_bootstrap_ever_used; - } - res.database_size = m_core.get_blockchain_storage().get_db().get_database_size(); - if (restricted) - res.database_size = round_up(res.database_size, 5ull * 1024 * 1024 * 1024); - res.update_available = restricted ? false : m_core.is_update_available(); - res.version = restricted ? "" : MONERO_VERSION; - return true; + return on_get_info(req, res, ctx); } //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_hard_fork_info(const COMMAND_RPC_HARD_FORK_INFO::request& req, COMMAND_RPC_HARD_FORK_INFO::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx) @@ -2436,9 +2410,9 @@ namespace cryptonote , "" }; - const command_line::arg_descriptor<std::vector<std::string>> 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<std::string> 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<std::vector<std::string>> core_rpc_server::arg_rpc_ssl_allowed_fingerprints = { @@ -2448,7 +2422,7 @@ namespace cryptonote const command_line::arg_descriptor<bool> 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<std::string> arg_rpc_ssl; static const command_line::arg_descriptor<std::string> arg_rpc_ssl_private_key; static const command_line::arg_descriptor<std::string> arg_rpc_ssl_certificate; - static const command_line::arg_descriptor<std::vector<std::string>> arg_rpc_ssl_allowed_certificates; + static const command_line::arg_descriptor<std::string> arg_rpc_ssl_ca_certificates; static const command_line::arg_descriptor<std::vector<std::string>> arg_rpc_ssl_allowed_fingerprints; static const command_line::arg_descriptor<bool> arg_rpc_ssl_allow_any_cert; static const command_line::arg_descriptor<std::string> arg_bootstrap_daemon_address; diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 8974bd1e0..9f7cc9c3b 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -6654,7 +6654,7 @@ bool simple_wallet::accept_loaded_tx(const std::function<size_t()> get_num_txes, { const tx_destination_entry &entry = cd.splitted_dsts[d]; std::string address, standard_address = get_account_address_as_str(m_wallet->nettype(), entry.is_subaddress, entry.addr); - if (has_encrypted_payment_id && !entry.is_subaddress) + if (has_encrypted_payment_id && !entry.is_subaddress && standard_address != entry.original) { address = get_account_integrated_address_as_str(m_wallet->nettype(), entry.addr, payment_id8); address += std::string(" (" + standard_address + " with encrypted payment id " + epee::string_tools::pod_to_hex(payment_id8) + ")"); diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 0e82f1a91..1d3ba900d 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -243,9 +243,10 @@ struct options { const command_line::arg_descriptor<std::string> daemon_ssl = {"daemon-ssl", tools::wallet2::tr("Enable SSL on daemon RPC connections: enabled|disabled|autodetect"), "autodetect"}; const command_line::arg_descriptor<std::string> daemon_ssl_private_key = {"daemon-ssl-private-key", tools::wallet2::tr("Path to a PEM format private key"), ""}; const command_line::arg_descriptor<std::string> daemon_ssl_certificate = {"daemon-ssl-certificate", tools::wallet2::tr("Path to a PEM format certificate"), ""}; - const command_line::arg_descriptor<std::vector<std::string>> 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<std::string> 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<std::vector<std::string>> daemon_ssl_allowed_fingerprints = {"daemon-ssl-allowed-fingerprints", tools::wallet2::tr("List of valid fingerprints of allowed RPC servers")}; const command_line::arg_descriptor<bool> 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<bool> daemon_ssl_allow_chained = {"daemon-ssl-allow-chained", tools::wallet2::tr("Allow user (via --daemon-ssl-ca-certificates) chain certificates"), false}; const command_line::arg_descriptor<bool> testnet = {"testnet", tools::wallet2::tr("For testnet. Daemon must also be launched with --testnet flag"), false}; const command_line::arg_descriptor<bool> stagenet = {"stagenet", tools::wallet2::tr("For stagenet. Daemon must also be launched with --stagenet flag"), false}; const command_line::arg_descriptor<std::string, false, true, 2> shared_ringdb_dir = { @@ -314,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); 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_host = command_line::get_arg(vm, opts.daemon_host); auto daemon_port = command_line::get_arg(vm, opts.daemon_port); @@ -321,13 +323,37 @@ std::unique_ptr<tools::wallet2> 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); - epee::net_utils::ssl_support_t ssl_support; - THROW_WALLET_EXCEPTION_IF(!epee::net_utils::ssl_support_from_string(ssl_support, daemon_ssl), tools::error::wallet_internal_error, - tools::wallet2::tr("Invalid argument for ") + std::string(opts.daemon_ssl.name)); + + // user specified CA file or fingeprints implies enabled SSL by default + epee::net_utils::ssl_options_t ssl_options = epee::net_utils::ssl_support_t::e_ssl_support_enabled; + if (command_line::get_arg(vm, opts.daemon_ssl_allow_any_cert)) + ssl_options.verification = epee::net_utils::ssl_verification_t::none; + else if (!daemon_ssl_ca_file.empty() || !daemon_ssl_allowed_fingerprints.empty()) + { + std::vector<std::vector<uint8_t>> 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); + + ssl_options = epee::net_utils::ssl_options_t{ + std::move(ssl_allowed_fingerprints), std::move(daemon_ssl_ca_file) + }; + + if (command_line::get_arg(vm, opts.daemon_ssl_allow_chained)) + ssl_options.verification = epee::net_utils::ssl_verification_t::user_ca; + } + + if (ssl_options.verification != epee::net_utils::ssl_verification_t::user_certificates || !command_line::is_arg_defaulted(vm, opts.daemon_ssl)) + { + THROW_WALLET_EXCEPTION_IF(!epee::net_utils::ssl_support_from_string(ssl_options.support, daemon_ssl), tools::error::wallet_internal_error, + tools::wallet2::tr("Invalid argument for ") + std::string(opts.daemon_ssl.name)); + } + + ssl_options.auth = epee::net_utils::ssl_authentication_t{ + std::move(daemon_ssl_private_key), std::move(daemon_ssl_certificate) + }; THROW_WALLET_EXCEPTION_IF(!daemon_address.empty() && !daemon_host.empty() && 0 != daemon_port, tools::error::wallet_internal_error, tools::wallet2::tr("can't specify daemon host or port more than once")); @@ -357,22 +383,24 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl if (daemon_address.empty()) 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(':')); - // onion and i2p addresses contain information about the server cert - // 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(); + const bool verification_required = + ssl_options.support == epee::net_utils::ssl_support_t::e_ssl_support_enabled || use_proxy; + THROW_WALLET_EXCEPTION_IF( - unencrypted_proxy, + verification_required && !ssl_options.has_strong_verification(real_daemon), 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" + 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); @@ -416,22 +444,8 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl catch (const std::exception &e) { } } - std::list<std::string> 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<std::vector<uint8_t>> 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<tools::wallet2> 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, std::move(ssl_options)); 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,9 +1114,10 @@ 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.daemon_ssl_allow_chained); command_line::add_arg(desc_params, opts.testnet); command_line::add_arg(desc_params, opts.stagenet); command_line::add_arg(desc_params, opts.shared_ringdb_dir); @@ -1154,10 +1169,7 @@ std::unique_ptr<wallet2> wallet2::make_dummy(const boost::program_options::varia } //---------------------------------------------------------------------------------------------------- -bool wallet2::set_daemon(std::string daemon_address, boost::optional<epee::net_utils::http::login> daemon_login, bool trusted_daemon, - epee::net_utils::ssl_support_t ssl_support, const std::pair<std::string, std::string> &private_key_and_certificate_path, - const std::list<std::string> &allowed_certificates, const std::vector<std::vector<uint8_t>> &allowed_fingerprints, - bool allow_any_cert) +bool wallet2::set_daemon(std::string daemon_address, boost::optional<epee::net_utils::http::login> daemon_login, bool trusted_daemon, epee::net_utils::ssl_options_t ssl_options) { if(m_http_client.is_connected()) m_http_client.disconnect(); @@ -1166,17 +1178,17 @@ bool wallet2::set_daemon(std::string daemon_address, boost::optional<epee::net_u m_trusted_daemon = trusted_daemon; MINFO("setting daemon to " << get_daemon_address()); - return m_http_client.set_server(get_daemon_address(), get_daemon_login(), ssl_support, private_key_and_certificate_path, allowed_certificates, allowed_fingerprints, allow_any_cert); + return m_http_client.set_server(get_daemon_address(), get_daemon_login(), std::move(ssl_options)); } //---------------------------------------------------------------------------------------------------- -bool wallet2::init(std::string daemon_address, boost::optional<epee::net_utils::http::login> 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<std::string, std::string> &private_key_and_certificate_path, const std::list<std::string> &allowed_certificates, const std::vector<std::vector<uint8_t>> &allowed_fingerprints, bool allow_any_cert) +bool wallet2::init(std::string daemon_address, boost::optional<epee::net_utils::http::login> daemon_login, boost::asio::ip::tcp::endpoint proxy, uint64_t upper_transaction_weight_limit, bool trusted_daemon, epee::net_utils::ssl_options_t ssl_options) { 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, std::move(ssl_options)); } //---------------------------------------------------------------------------------------------------- bool wallet2::is_deterministic() const @@ -2275,6 +2287,12 @@ void wallet2::process_outgoing(const crypto::hash &txid, const cryptonote::trans add_rings(tx); } //---------------------------------------------------------------------------------------------------- +bool wallet2::should_skip_block(const cryptonote::block &b, uint64_t height) const +{ + // seeking only for blocks that are not older then the wallet creation time plus 1 day. 1 day is for possible user incorrect time setup + return !(b.timestamp + 60*60*24 > m_account.get_createtime() && height >= m_refresh_from_block_height); +} +//---------------------------------------------------------------------------------------------------- void wallet2::process_new_blockchain_entry(const cryptonote::block& b, const cryptonote::block_complete_entry& bche, const parsed_block &parsed_block, const crypto::hash& bl_id, uint64_t height, const std::vector<tx_cache_data> &tx_cache_data, size_t tx_cache_data_offset, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache) { THROW_WALLET_EXCEPTION_IF(bche.txs.size() + 1 != parsed_block.o_indices.indices.size(), error::wallet_internal_error, @@ -2284,7 +2302,7 @@ void wallet2::process_new_blockchain_entry(const cryptonote::block& b, const cry //handle transactions from new block //optimization: seeking only for blocks that are not older then the wallet creation time plus 1 day. 1 day is for possible user incorrect time setup - if(b.timestamp + 60*60*24 > m_account.get_createtime() && height >= m_refresh_from_block_height) + if (!should_skip_block(b, height)) { TIME_MEASURE_START(miner_tx_handle_time); if (m_refresh_type != RefreshNoCoinbase) @@ -2348,9 +2366,7 @@ void wallet2::get_short_chain_history(std::list<crypto::hash>& ids, uint64_t gra //---------------------------------------------------------------------------------------------------- void wallet2::parse_block_round(const cryptonote::blobdata &blob, cryptonote::block &bl, crypto::hash &bl_id, bool &error) const { - error = !cryptonote::parse_and_validate_block_from_blob(blob, bl); - if (!error) - bl_id = get_block_hash(bl); + error = !cryptonote::parse_and_validate_block_from_blob(blob, bl, bl_id); } //---------------------------------------------------------------------------------------------------- void wallet2::pull_blocks(uint64_t start_height, uint64_t &blocks_start_height, const std::list<crypto::hash> &short_chain_history, std::vector<cryptonote::block_complete_entry> &blocks, std::vector<cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices> &o_indices) @@ -2416,6 +2432,11 @@ void wallet2::process_parsed_blocks(uint64_t start_height, const std::vector<cry { THROW_WALLET_EXCEPTION_IF(parsed_blocks[i].txes.size() != parsed_blocks[i].block.tx_hashes.size(), error::wallet_internal_error, "Mismatched parsed_blocks[i].txes.size() and parsed_blocks[i].block.tx_hashes.size()"); + if (should_skip_block(parsed_blocks[i].block, start_height + i)) + { + txidx += 1 + parsed_blocks[i].block.tx_hashes.size(); + continue; + } if (m_refresh_type != RefreshNoCoinbase) tpool.submit(&waiter, [&, i, txidx](){ cache_tx_data(parsed_blocks[i].block.miner_tx, get_transaction_hash(parsed_blocks[i].block.miner_tx), tx_cache_data[txidx]); }); ++txidx; @@ -2444,6 +2465,8 @@ void wallet2::process_parsed_blocks(uint64_t start_height, const std::vector<cry for (size_t i = 0; i < tx_cache_data.size(); ++i) { + if (tx_cache_data[i].empty()) + continue; tpool.submit(&waiter, [&hwdev, &gender, &tx_cache_data, i]() { auto &slot = tx_cache_data[i]; boost::unique_lock<hw::device> hwdev_lock(hwdev); @@ -2462,6 +2485,7 @@ void wallet2::process_parsed_blocks(uint64_t start_height, const std::vector<cry if (o.target.type() == typeid(cryptonote::txout_to_key)) { std::vector<crypto::key_derivation> additional_derivations; + additional_derivations.reserve(tx_cache_data[txidx].additional.size()); for (const auto &iod: tx_cache_data[txidx].additional) additional_derivations.push_back(iod.derivation); const auto &key = boost::get<txout_to_key>(o.target).key; @@ -2479,6 +2503,12 @@ void wallet2::process_parsed_blocks(uint64_t start_height, const std::vector<cry txidx = 0; for (size_t i = 0; i < blocks.size(); ++i) { + if (should_skip_block(parsed_blocks[i].block, start_height + i)) + { + txidx += 1 + parsed_blocks[i].block.tx_hashes.size(); + continue; + } + if (m_refresh_type != RefreshType::RefreshNoCoinbase) { THROW_WALLET_EXCEPTION_IF(txidx >= tx_cache_data.size(), error::wallet_internal_error, "txidx out of range"); @@ -4128,6 +4158,17 @@ bool wallet2::query_device(hw::device::device_type& device_type, const std::stri return true; } +void wallet2::init_type(hw::device::device_type device_type) +{ + m_account_public_address = m_account.get_keys().m_account_address; + m_watch_only = false; + m_multisig = false; + m_multisig_threshold = 0; + m_multisig_signers.clear(); + m_original_keys_available = false; + m_key_device_type = device_type; +} + /*! * \brief Generates a wallet or restores one. * \param wallet_ Name of wallet file @@ -4197,18 +4238,15 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string& m_account.make_multisig(view_secret_key, spend_secret_key, spend_public_key, multisig_keys); m_account.finalize_multisig(spend_public_key); - m_account_public_address = m_account.get_keys().m_account_address; - m_watch_only = false; + // Not possible to restore a multisig wallet that is able to activate the MMS + // (because the original keys are not (yet) part of the restore info), so + // keep m_original_keys_available to false + init_type(hw::device::device_type::SOFTWARE); m_multisig = true; m_multisig_threshold = threshold; m_multisig_signers = multisig_signers; - m_key_device_type = hw::device::device_type::SOFTWARE; setup_keys(password); - // Not possible to restore a multisig wallet that is able to activate the MMS - // (because the original keys are not (yet) part of the restore info) - m_original_keys_available = false; - create_keys_file(wallet_, false, password, m_nettype != MAINNET || create_address_file); setup_new_blockchain(); @@ -4241,13 +4279,7 @@ crypto::secret_key wallet2::generate(const std::string& wallet_, const epee::wip crypto::secret_key retval = m_account.generate(recovery_param, recover, two_random); - m_account_public_address = m_account.get_keys().m_account_address; - m_watch_only = false; - m_multisig = false; - m_multisig_threshold = 0; - m_multisig_signers.clear(); - m_original_keys_available = false; - m_key_device_type = hw::device::device_type::SOFTWARE; + init_type(hw::device::device_type::SOFTWARE); setup_keys(password); // calculate a starting refresh height @@ -4330,13 +4362,9 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string& } m_account.create_from_viewkey(account_public_address, viewkey); - m_account_public_address = account_public_address; + init_type(hw::device::device_type::SOFTWARE); m_watch_only = true; - m_multisig = false; - m_multisig_threshold = 0; - m_multisig_signers.clear(); - m_original_keys_available = false; - m_key_device_type = hw::device::device_type::SOFTWARE; + m_account_public_address = account_public_address; setup_keys(password); create_keys_file(wallet_, true, password, m_nettype != MAINNET || create_address_file); @@ -4371,13 +4399,8 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string& } m_account.create_from_keys(account_public_address, spendkey, viewkey); + init_type(hw::device::device_type::SOFTWARE); m_account_public_address = account_public_address; - m_watch_only = false; - m_multisig = false; - m_multisig_threshold = 0; - m_multisig_signers.clear(); - m_original_keys_available = false; - m_key_device_type = hw::device::device_type::SOFTWARE; setup_keys(password); create_keys_file(wallet_, false, password, create_address_file); @@ -4412,13 +4435,7 @@ void wallet2::restore(const std::string& wallet_, const epee::wipeable_string& p hwdev.set_callback(get_device_callback()); m_account.create_from_device(hwdev); - m_key_device_type = m_account.get_device().get_type(); - m_account_public_address = m_account.get_keys().m_account_address; - m_watch_only = false; - m_multisig = false; - m_multisig_threshold = 0; - m_multisig_signers.clear(); - m_original_keys_available = false; + init_type(m_account.get_device().get_type()); setup_keys(password); m_device_name = device_name; @@ -4550,10 +4567,9 @@ std::string wallet2::make_multisig(const epee::wipeable_string &password, "Failed to create multisig wallet due to bad keys"); memwipe(&spend_skey, sizeof(rct::key)); - m_account_public_address = m_account.get_keys().m_account_address; - m_watch_only = false; + init_type(hw::device::device_type::SOFTWARE); + m_original_keys_available = true; m_multisig = true; - m_key_device_type = hw::device::device_type::SOFTWARE; m_multisig_threshold = threshold; m_multisig_signers = multisig_signers; ++m_multisig_rounds_passed; @@ -10584,13 +10600,13 @@ void wallet2::check_tx_key_helper(const crypto::hash &txid, const crypto::key_de check_tx_key_helper(tx, derivation, additional_derivations, address, received); in_pool = res.txs.front().in_pool; - confirmations = (uint64_t)-1; + confirmations = 0; if (!in_pool) { std::string err; uint64_t bc_height = get_daemon_blockchain_height(err); if (err.empty()) - confirmations = bc_height - (res.txs.front().block_height + 1); + confirmations = bc_height - res.txs.front().block_height; } } @@ -10786,13 +10802,13 @@ bool wallet2::check_tx_proof(const crypto::hash &txid, const cryptonote::account return false; in_pool = res.txs.front().in_pool; - confirmations = (uint64_t)-1; + confirmations = 0; if (!in_pool) { std::string err; uint64_t bc_height = get_daemon_blockchain_height(err); if (err.empty()) - confirmations = bc_height - (res.txs.front().block_height + 1); + confirmations = bc_height - res.txs.front().block_height; } return true; diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 2eb7de94a..38cd68784 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -533,6 +533,8 @@ namespace tools std::vector<cryptonote::tx_extra_field> tx_extra_fields; std::vector<is_out_data> primary; std::vector<is_out_data> additional; + + bool empty() const { return tx_extra_fields.empty() && primary.empty() && additional.empty(); } }; /*! @@ -687,16 +689,10 @@ namespace tools boost::asio::ip::tcp::endpoint proxy = {}, uint64_t upper_transaction_weight_limit = 0, 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<std::string, std::string> &private_key_and_certificate_path = {}, - const std::list<std::string> &allowed_certificates = {}, const std::vector<std::vector<uint8_t>> &allowed_fingerprints = {}, - bool allow_any_cert = false); + epee::net_utils::ssl_options_t ssl_options = epee::net_utils::ssl_support_t::e_ssl_support_autodetect); bool set_daemon(std::string daemon_address = "http://localhost:8080", boost::optional<epee::net_utils::http::login> 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<std::string, std::string> &private_key_and_certificate_path = {}, - const std::list<std::string> &allowed_certificates = {}, const std::vector<std::vector<uint8_t>> &allowed_fingerprints = {}, - bool allow_any_cert = false); + epee::net_utils::ssl_options_t ssl_options = epee::net_utils::ssl_support_t::e_ssl_support_autodetect); void stop() { m_run.store(false, std::memory_order_relaxed); m_message_store.stop(); } @@ -1296,6 +1292,7 @@ namespace tools */ bool load_keys(const std::string& keys_file_name, const epee::wipeable_string& password); void process_new_transaction(const crypto::hash &txid, const cryptonote::transaction& tx, const std::vector<uint64_t> &o_indices, uint64_t height, uint64_t ts, bool miner_tx, bool pool, bool double_spend_seen, const tx_cache_data &tx_cache_data, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache = NULL); + bool should_skip_block(const cryptonote::block &b, uint64_t height) const; void process_new_blockchain_entry(const cryptonote::block& b, const cryptonote::block_complete_entry& bche, const parsed_block &parsed_block, const crypto::hash& bl_id, uint64_t height, const std::vector<tx_cache_data> &tx_cache_data, size_t tx_cache_data_offset, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache = NULL); void detach_blockchain(uint64_t height, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache = NULL); void get_short_chain_history(std::list<crypto::hash>& ids, uint64_t granularity = 1) const; @@ -1362,6 +1359,7 @@ namespace tools void cache_tx_data(const cryptonote::transaction& tx, const crypto::hash &txid, tx_cache_data &tx_cache_data) const; std::shared_ptr<std::map<std::pair<uint64_t, uint64_t>, size_t>> create_output_tracker_cache() const; + void init_type(hw::device::device_type device_type); void setup_new_blockchain(); void create_keys_file(const std::string &wallet_, bool watch_only, const epee::wipeable_string &password, bool create_address_file); diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 95cda7f1e..d456797a9 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<std::string> arg_rpc_ssl = {"rpc-ssl", tools::wallet2::tr("Enable SSL on wallet RPC connections: enabled|disabled|autodetect"), "autodetect"}; const command_line::arg_descriptor<std::string> arg_rpc_ssl_private_key = {"rpc-ssl-private-key", tools::wallet2::tr("Path to a PEM format private key"), ""}; const command_line::arg_descriptor<std::string> arg_rpc_ssl_certificate = {"rpc-ssl-certificate", tools::wallet2::tr("Path to a PEM format certificate"), ""}; - const command_line::arg_descriptor<std::vector<std::string>> 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<std::string> 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<std::vector<std::string>> 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,28 +247,34 @@ 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; - if (!epee::net_utils::ssl_support_from_string(rpc_ssl_support, rpc_ssl)) + epee::net_utils::ssl_options_t rpc_ssl_options = epee::net_utils::ssl_support_t::e_ssl_support_enabled; + + if (!rpc_ssl_ca_file.empty() || !rpc_ssl_allowed_fingerprints.empty()) { - MERROR("Invalid argument for " << std::string(arg_rpc_ssl.name)); - return false; + std::vector<std::vector<uint8_t>> 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); + + rpc_ssl_options = epee::net_utils::ssl_options_t{ + std::move(allowed_fingerprints), std::move(rpc_ssl_ca_file) + }; } - std::list<std::string> allowed_certificates; - for (const std::string &path: rpc_ssl_allowed_certificates) + + // user specified CA file or fingeprints implies enabled SSL by default + if (rpc_ssl_options.verification != epee::net_utils::ssl_verification_t::user_certificates || !command_line::is_arg_defaulted(vm, arg_rpc_ssl)) { - 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(); - } + if (!epee::net_utils::ssl_support_from_string(rpc_ssl_options.support, rpc_ssl)) + { + MERROR("Invalid argument for " << std::string(arg_rpc_ssl.name)); + return false; + } } - std::vector<std::vector<uint8_t>> 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); + rpc_ssl_options.auth = epee::net_utils::ssl_authentication_t{ + std::move(rpc_ssl_private_key), std::move(rpc_ssl_certificate) + }; m_auto_refresh_period = DEFAULT_AUTO_REFRESH_PERIOD; m_last_auto_refresh_time = boost::posix_time::min_date_time; @@ -277,7 +283,7 @@ namespace tools auto rng = [](size_t len, uint8_t *ptr) { return crypto::rand(len, ptr); }; return epee::http_server_impl_base<wallet_rpc_server, connection_context>::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) + std::move(rpc_ssl_options) ); } //------------------------------------------------------------------------------------------------------------------------------ @@ -1173,7 +1179,7 @@ namespace tools { const cryptonote::tx_destination_entry &entry = cd.splitted_dsts[d]; std::string address = cryptonote::get_account_address_as_str(m_wallet->nettype(), entry.is_subaddress, entry.addr); - if (has_encrypted_payment_id && !entry.is_subaddress) + if (has_encrypted_payment_id && !entry.is_subaddress && address != entry.original) address = cryptonote::get_account_integrated_address_as_str(m_wallet->nettype(), entry.addr, payment_id8); auto i = dests.find(entry.addr); if (i == dests.end()) @@ -2916,7 +2922,8 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_get_languages(const wallet_rpc::COMMAND_RPC_GET_LANGUAGES::request& req, wallet_rpc::COMMAND_RPC_GET_LANGUAGES::response& res, epee::json_rpc::error& er, const connection_context *ctx) { - crypto::ElectrumWords::get_language_list(res.languages); + crypto::ElectrumWords::get_language_list(res.languages, true); + crypto::ElectrumWords::get_language_list(res.languages_local, false); return true; } //------------------------------------------------------------------------------------------------------------------------------ @@ -2947,14 +2954,19 @@ namespace tools std::string wallet_file = req.filename.empty() ? "" : (m_wallet_dir + "/" + req.filename); { std::vector<std::string> languages; - crypto::ElectrumWords::get_language_list(languages); + crypto::ElectrumWords::get_language_list(languages, false); std::vector<std::string>::iterator it; it = std::find(languages.begin(), languages.end(), req.language); if (it == languages.end()) { + crypto::ElectrumWords::get_language_list(languages, true); + it = std::find(languages.begin(), languages.end(), req.language); + } + if (it == languages.end()) + { er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; - er.message = "Unknown language"; + er.message = "Unknown language: " + req.language; return false; } } @@ -4048,13 +4060,7 @@ namespace tools er.message = "Command unavailable in restricted mode."; return false; } - epee::net_utils::ssl_support_t ssl_support; - if (!epee::net_utils::ssl_support_from_string(ssl_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; ssl_allowed_fingerprints.reserve(req.ssl_allowed_fingerprints.size()); for (const std::string &fp: req.ssl_allowed_fingerprints) @@ -4064,7 +4070,31 @@ 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)) + + epee::net_utils::ssl_options_t ssl_options = epee::net_utils::ssl_support_t::e_ssl_support_enabled; + if (req.ssl_allow_any_cert) + ssl_options.verification = epee::net_utils::ssl_verification_t::none; + 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)}; + + 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{ + 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))) { er.code = WALLET_RPC_ERROR_CODE_NO_DAEMON_CONNECTION; er.message = std::string("Unable to set daemon"); @@ -4272,7 +4302,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 298f34f66..4c945ab41 100644 --- a/src/wallet/wallet_rpc_server_commands_defs.h +++ b/src/wallet/wallet_rpc_server_commands_defs.h @@ -47,7 +47,7 @@ // advance which version they will stop working with // Don't go over 32767 for any of these #define WALLET_RPC_VERSION_MAJOR 1 -#define WALLET_RPC_VERSION_MINOR 8 +#define WALLET_RPC_VERSION_MINOR 9 #define MAKE_WALLET_RPC_VERSION(major,minor) (((major)<<16)|(minor)) #define WALLET_RPC_VERSION MAKE_WALLET_RPC_VERSION(WALLET_RPC_VERSION_MAJOR, WALLET_RPC_VERSION_MINOR) namespace tools @@ -1999,9 +1999,11 @@ namespace wallet_rpc struct response_t { std::vector<std::string> languages; + std::vector<std::string> languages_local; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(languages) + KV_SERIALIZE(languages_local) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<response_t> response; @@ -2446,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<std::string> ssl_allowed_certificates; + std::string ssl_ca_file; std::vector<std::string> ssl_allowed_fingerprints; bool ssl_allow_any_cert; @@ -2456,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() diff --git a/tests/trezor/trezor_tests.cpp b/tests/trezor/trezor_tests.cpp index 310fa45f1..8d5540328 100644 --- a/tests/trezor/trezor_tests.cpp +++ b/tests/trezor/trezor_tests.cpp @@ -1406,7 +1406,7 @@ tsx_builder * tsx_builder::construct_pending_tx(tools::wallet2::pending_tx &ptx, ptx.construction_data.extra = tx.extra; ptx.construction_data.unlock_time = 0; ptx.construction_data.use_rct = true; - ptx.construction_data.use_bulletproofs = true; + ptx.construction_data.rct_config = m_rct_config; ptx.construction_data.dests = m_destinations_orig; ptx.construction_data.subaddr_account = 0; diff --git a/tests/unit_tests/hmac_keccak.cpp b/tests/unit_tests/hmac_keccak.cpp index cb35d272a..3898d4a7a 100644 --- a/tests/unit_tests/hmac_keccak.cpp +++ b/tests/unit_tests/hmac_keccak.cpp @@ -88,7 +88,8 @@ static void test_keccak_hmac(const size_t * chunks) uint8_t key_buff[1024]; uint8_t res_exp[32]; uint8_t res_comp[32]; - const size_t len_chunks = chunks ? sizeof(chunks) / sizeof(*chunks) : 0; + size_t len_chunks = 0; + for(; chunks && chunks[len_chunks] > 0; ++len_chunks); for (size_t i = 0; i < (sizeof(keccak_hmac_vectors) / sizeof(*keccak_hmac_vectors)); i++) { @@ -124,29 +125,29 @@ static void test_keccak_hmac(const size_t * chunks) TEST(keccak_hmac, ) { - test_keccak_hmac({}); + test_keccak_hmac(nullptr); } TEST(keccak_hmac, 1) { - static const size_t chunks[] = {1}; + static const size_t chunks[] = {1, 0}; test_keccak_hmac(chunks); } TEST(keccak_hmac, 1_20) { - static const size_t chunks[] = {1, 20}; + static const size_t chunks[] = {1, 20, 0}; test_keccak_hmac(chunks); } TEST(keccak_hmac, 136_1) { - static const size_t chunks[] = {136, 1}; + static const size_t chunks[] = {136, 1, 0}; test_keccak_hmac(chunks); } TEST(keccak_hmac, 137_1) { - static const size_t chunks[] = {137, 1}; + static const size_t chunks[] = {137, 1, 0}; test_keccak_hmac(chunks); } diff --git a/tests/unit_tests/long_term_block_weight.cpp b/tests/unit_tests/long_term_block_weight.cpp index ce6ff3551..bf1368618 100644 --- a/tests/unit_tests/long_term_block_weight.cpp +++ b/tests/unit_tests/long_term_block_weight.cpp @@ -76,6 +76,11 @@ public: while (count-- && start_height < blocks.size()) ret.push_back(blocks[start_height++].long_term_weight); return ret; } + virtual crypto::hash get_block_hash_from_height(const uint64_t &height) const override { + crypto::hash hash = crypto::null_hash; + *(uint64_t*)&hash = height; + return hash; + } virtual crypto::hash top_block_hash(uint64_t *block_height = NULL) const override { uint64_t h = height(); crypto::hash top = crypto::null_hash; |