commit
5bbbe3902b
@ -32,6 +32,7 @@
|
||||
#include <cstdint>
|
||||
#include <iosfwd>
|
||||
#include <string>
|
||||
#include <boost/utility/string_ref.hpp>
|
||||
|
||||
#include "wipeable_string.h"
|
||||
#include "span.h"
|
||||
@ -68,4 +69,10 @@ namespace epee
|
||||
//! Write `src` bytes as hex to `out`. `out` must be twice the length
|
||||
static void buffer_unchecked(char* out, const span<const std::uint8_t> src) noexcept;
|
||||
};
|
||||
|
||||
struct from_hex
|
||||
{
|
||||
//! \return An std::vector of unsigned integers from the `src`
|
||||
static std::vector<uint8_t> vector(boost::string_ref src);
|
||||
};
|
||||
}
|
||||
|
@ -228,8 +228,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 = {}, 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 = {}, bool allow_any_cert = false);
|
||||
bool init_server(uint32_t port, const std::string address = "0.0.0.0", epee::net_utils::ssl_support_t ssl_support = epee::net_utils::ssl_support_t::e_ssl_support_autodetect, const std::pair<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);
|
||||
|
||||
/// 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());
|
||||
|
@ -900,7 +900,7 @@ PRAGMA_WARNING_DISABLE_VS(4355)
|
||||
m_thread_index(0),
|
||||
m_connection_type( connection_type ),
|
||||
new_connection_(),
|
||||
m_ssl_context({boost::asio::ssl::context(boost::asio::ssl::context::sslv23), {}})
|
||||
m_ssl_context({boost::asio::ssl::context(boost::asio::ssl::context::tlsv12), {}})
|
||||
{
|
||||
create_server_type_map();
|
||||
m_thread_name_prefix = "NET";
|
||||
@ -939,14 +939,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, bool allow_any_cert)
|
||||
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)
|
||||
{
|
||||
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, allow_any_cert);
|
||||
m_ssl_context = create_ssl_context(private_key_and_certificate_path, allowed_certificates, allowed_fingerprints, allow_any_cert);
|
||||
// Open the acceptor with the option to reuse the address (i.e. SO_REUSEADDR).
|
||||
boost::asio::ip::tcp::resolver resolver(io_service_);
|
||||
boost::asio::ip::tcp::resolver::query query(address, boost::lexical_cast<std::string>(port), boost::asio::ip::tcp::resolver::query::canonical_name);
|
||||
@ -980,7 +980,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, bool allow_any_cert)
|
||||
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)
|
||||
{
|
||||
uint32_t p = 0;
|
||||
|
||||
@ -988,7 +988,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, allow_any_cert);
|
||||
return this->init_server(p, address, ssl_support, private_key_and_certificate_path, allowed_certificates, allowed_fingerprints, allow_any_cert);
|
||||
}
|
||||
POP_WARNINGS
|
||||
//---------------------------------------------------------------------------------
|
||||
|
@ -278,6 +278,7 @@ namespace net_utils
|
||||
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:
|
||||
@ -302,16 +303,16 @@ namespace net_utils
|
||||
const std::string &get_host() const { return m_host_buff; };
|
||||
const std::string &get_port() const { return m_port; };
|
||||
|
||||
bool set_server(const std::string& address, boost::optional<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 = {}, bool allow_any_cert = false)
|
||||
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)
|
||||
{
|
||||
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, allow_any_cert);
|
||||
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);
|
||||
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 = {}, bool allow_any_cert = false)
|
||||
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)
|
||||
{
|
||||
CRITICAL_REGION_LOCAL(m_lock);
|
||||
disconnect();
|
||||
@ -321,8 +322,9 @@ namespace net_utils
|
||||
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_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);
|
||||
}
|
||||
|
||||
bool connect(std::chrono::milliseconds timeout)
|
||||
|
@ -61,7 +61,9 @@ namespace epee
|
||||
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 = {},
|
||||
const std::list<std::string> &allowed_certificates = std::list<std::string>(), bool allow_any_cert = false)
|
||||
std::list<std::string> allowed_certificates = {},
|
||||
std::vector<std::vector<uint8_t>> allowed_fingerprints = {},
|
||||
bool allow_any_cert = false)
|
||||
{
|
||||
|
||||
//set self as callback handler
|
||||
@ -78,7 +80,7 @@ namespace epee
|
||||
m_net_server.get_config_object().m_user = std::move(user);
|
||||
|
||||
MGINFO("Binding on " << bind_ip << ":" << bind_port);
|
||||
bool res = m_net_server.init_server(bind_port, bind_ip, ssl_support, private_key_and_certificate_path, allowed_certificates, allow_any_cert);
|
||||
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);
|
||||
if(!res)
|
||||
{
|
||||
LOG_ERROR("Failed to bind server");
|
||||
|
@ -93,7 +93,7 @@ namespace net_utils
|
||||
m_deadline(m_io_service),
|
||||
m_shutdowned(0),
|
||||
m_ssl_support(epee::net_utils::ssl_support_t::e_ssl_support_autodetect),
|
||||
m_ctx({boost::asio::ssl::context(boost::asio::ssl::context::sslv23), {}}),
|
||||
m_ctx({boost::asio::ssl::context(boost::asio::ssl::context::tlsv12), {}}),
|
||||
m_ssl_socket(new boost::asio::ssl::stream<boost::asio::ip::tcp::socket>(m_io_service,m_ctx.context))
|
||||
{
|
||||
|
||||
@ -118,12 +118,12 @@ namespace net_utils
|
||||
catch(...) { /* ignore */ }
|
||||
}
|
||||
|
||||
inline void set_ssl(epee::net_utils::ssl_support_t ssl_support = epee::net_utils::ssl_support_t::e_ssl_support_autodetect, const std::pair<std::string, std::string> &private_key_and_certificate_path = {}, const std::list<std::string> &allowed_certificates = std::list<std::string>(), bool allow_any_cert = false)
|
||||
inline void set_ssl(epee::net_utils::ssl_support_t ssl_support = epee::net_utils::ssl_support_t::e_ssl_support_autodetect, const std::pair<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)
|
||||
{
|
||||
if (ssl_support == epee::net_utils::ssl_support_t::e_ssl_support_disabled)
|
||||
m_ctx = {boost::asio::ssl::context(boost::asio::ssl::context::sslv23), {}};
|
||||
m_ctx = {boost::asio::ssl::context(boost::asio::ssl::context::tlsv12), {}, {}};
|
||||
else
|
||||
m_ctx = create_ssl_context(private_key_and_certificate_path, allowed_certificates, allow_any_cert);
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -50,16 +50,17 @@ namespace net_utils
|
||||
{
|
||||
boost::asio::ssl::context context;
|
||||
std::list<std::string> allowed_certificates;
|
||||
std::vector<std::vector<uint8_t>> allowed_fingerprints;
|
||||
bool allow_any_cert;
|
||||
};
|
||||
|
||||
// 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, bool allow_any_cert);
|
||||
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 create_ssl_certificate(std::string &pkey_buffer, std::string &cert_buffer);
|
||||
bool is_certificate_allowed(boost::asio::ssl::verify_context &ctx, const std::list<std::string> &allowed_certificates);
|
||||
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);
|
||||
}
|
||||
|
@ -83,4 +83,67 @@ namespace epee
|
||||
{
|
||||
return write_hex(out, src);
|
||||
}
|
||||
|
||||
std::vector<uint8_t> from_hex::vector(boost::string_ref src)
|
||||
{
|
||||
// should we include a specific character
|
||||
auto include = [](char input) {
|
||||
// we ignore spaces and colons
|
||||
return !std::isspace(input) && input != ':';
|
||||
};
|
||||
|
||||
// the number of relevant characters to decode
|
||||
auto count = std::count_if(src.begin(), src.end(), include);
|
||||
|
||||
// this must be a multiple of two, otherwise we have a truncated input
|
||||
if (count % 2) {
|
||||
throw std::length_error{ "Invalid hexadecimal input length" };
|
||||
}
|
||||
|
||||
std::vector<uint8_t> result;
|
||||
result.reserve(count / 2);
|
||||
|
||||
// the data to work with (std::string is always null-terminated)
|
||||
auto data = src.data();
|
||||
|
||||
// convert a single hex character to an unsigned integer
|
||||
auto char_to_int = [](const char *input) {
|
||||
switch (std::tolower(*input)) {
|
||||
case '0': return 0;
|
||||
case '1': return 1;
|
||||
case '2': return 2;
|
||||
case '3': return 3;
|
||||
case '4': return 4;
|
||||
case '5': return 5;
|
||||
case '6': return 6;
|
||||
case '7': return 7;
|
||||
case '8': return 8;
|
||||
case '9': return 9;
|
||||
case 'a': return 10;
|
||||
case 'b': return 11;
|
||||
case 'c': return 12;
|
||||
case 'd': return 13;
|
||||
case 'e': return 14;
|
||||
case 'f': return 15;
|
||||
default: throw std::range_error{ "Invalid hexadecimal input" };
|
||||
}
|
||||
};
|
||||
|
||||
// keep going until we reach the end
|
||||
while (data[0] != '\0') {
|
||||
// skip unwanted characters
|
||||
if (!include(data[0])) {
|
||||
++data;
|
||||
continue;
|
||||
}
|
||||
|
||||
// convert two matching characters to int
|
||||
auto high = char_to_int(data++);
|
||||
auto low = char_to_int(data++);
|
||||
|
||||
result.push_back(high << 4 | low);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
@ -154,13 +154,19 @@ bool create_ssl_certificate(std::string &pkey_buffer, std::string &cert_buffer)
|
||||
return success;
|
||||
}
|
||||
|
||||
ssl_context_t create_ssl_context(const std::pair<std::string, std::string> &private_key_and_certificate_path, std::list<std::string> allowed_certificates, bool allow_any_cert)
|
||||
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_context_t ssl_context({boost::asio::ssl::context(boost::asio::ssl::context::sslv23), std::move(allowed_certificates)});
|
||||
ssl_context_t ssl_context{boost::asio::ssl::context(boost::asio::ssl::context::tlsv12), std::move(allowed_certificates), std::move(allowed_fingerprints)};
|
||||
|
||||
// disable sslv2
|
||||
ssl_context.context.set_options(boost::asio::ssl::context::default_workarounds | boost::asio::ssl::context::no_sslv2);
|
||||
ssl_context.context.set_default_verify_paths();
|
||||
// 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);
|
||||
|
||||
// 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");
|
||||
|
||||
// set options on the SSL context for added security
|
||||
SSL_CTX *ctx = ssl_context.context.native_handle();
|
||||
@ -179,7 +185,7 @@ 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_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1); // https://github.com/ssllabs/research/wiki/SSL-and-TLS-Deployment-Best-Practices
|
||||
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())
|
||||
@ -225,7 +231,7 @@ bool is_ssl(const unsigned char *data, size_t len)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool is_certificate_allowed(boost::asio::ssl::verify_context &ctx, const std::list<std::string> &allowed_certificates)
|
||||
bool is_certificate_allowed(boost::asio::ssl::verify_context &ctx, const ssl_context_t &ssl_context)
|
||||
{
|
||||
X509_STORE_CTX *sctx = ctx.native_handle();
|
||||
if (!sctx)
|
||||
@ -240,23 +246,51 @@ bool is_certificate_allowed(boost::asio::ssl::verify_context &ctx, const std::li
|
||||
return false;
|
||||
}
|
||||
|
||||
BIO *bio_cert = BIO_new(BIO_s_mem());
|
||||
openssl_bio bio_cert_deleter{bio_cert};
|
||||
bool success = PEM_write_bio_X509(bio_cert, cert);
|
||||
if (!success)
|
||||
{
|
||||
MERROR("Failed to print certificate");
|
||||
return false;
|
||||
// can we check the certificate against a list of fingerprints?
|
||||
if (!ssl_context.allowed_fingerprints.empty()) {
|
||||
// buffer for the certificate digest and the size of the result
|
||||
std::vector<uint8_t> digest(EVP_MAX_MD_SIZE);
|
||||
unsigned int size{ 0 };
|
||||
|
||||
// create the digest from the certificate
|
||||
if (!X509_digest(cert, EVP_sha1(), digest.data(), &size)) {
|
||||
MERROR("Failed to create certificate fingerprint");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
BUF_MEM *buf = NULL;
|
||||
BIO_get_mem_ptr(bio_cert, &buf);
|
||||
if (!buf || !buf->data || !buf->length)
|
||||
{
|
||||
MERROR("Failed to write certificate: " << ERR_get_error());
|
||||
return false;
|
||||
|
||||
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;
|
||||
}
|
||||
std::string certificate(std::string(buf->data, buf->length));
|
||||
return std::find(allowed_certificates.begin(), allowed_certificates.end(), certificate) != allowed_certificates.end();
|
||||
|
||||
// if either checklist is non-empty we must have failed it
|
||||
return ssl_context.allowed_fingerprints.empty() && ssl_context.allowed_certificates.empty();
|
||||
}
|
||||
|
||||
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)
|
||||
@ -276,7 +310,7 @@ bool ssl_handshake(boost::asio::ssl::stream<boost::asio::ip::tcp::socket> &socke
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!ssl_context.allow_any_cert && !ssl_context.allowed_certificates.empty() && !is_certificate_allowed(ctx, ssl_context.allowed_certificates))
|
||||
if (!ssl_context.allow_any_cert && !is_certificate_allowed(ctx, ssl_context))
|
||||
{
|
||||
MERROR("Certificate is not in the allowed list, connection droppped");
|
||||
return false;
|
||||
@ -289,7 +323,7 @@ bool ssl_handshake(boost::asio::ssl::stream<boost::asio::ip::tcp::socket> &socke
|
||||
socket.handshake(type, ec);
|
||||
if (ec)
|
||||
{
|
||||
MERROR("handshake failed, connection dropped");
|
||||
MERROR("handshake failed, connection dropped: " << ec.message());
|
||||
return false;
|
||||
}
|
||||
if (!ssl_context.allow_any_cert && !verified)
|
||||
|
@ -80,6 +80,7 @@ namespace cryptonote
|
||||
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_allowed_fingerprints);
|
||||
command_line::add_arg(desc, arg_rpc_ssl_allow_any_cert);
|
||||
command_line::add_arg(desc, arg_bootstrap_daemon_address);
|
||||
command_line::add_arg(desc, arg_bootstrap_daemon_login);
|
||||
@ -156,12 +157,16 @@ namespace cryptonote
|
||||
ssl_allowed_certificates.back() = std::string();
|
||||
}
|
||||
}
|
||||
|
||||
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), ssl_allowed_certificates, ssl_allow_any_cert
|
||||
ssl_support, std::make_pair(ssl_private_key, ssl_certificate), std::move(ssl_allowed_certificates), std::move(ssl_allowed_fingerprints), ssl_allow_any_cert
|
||||
);
|
||||
}
|
||||
//------------------------------------------------------------------------------------------------------------------------------
|
||||
@ -2369,6 +2374,11 @@ namespace cryptonote
|
||||
, "List of paths to PEM format certificates of allowed peers (all allowed if empty)"
|
||||
};
|
||||
|
||||
const command_line::arg_descriptor<std::vector<std::string>> core_rpc_server::arg_rpc_ssl_allowed_fingerprints = {
|
||||
"rpc-ssl-allowed-fingerprints"
|
||||
, "List of certificate fingerprints to allow"
|
||||
};
|
||||
|
||||
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"
|
||||
|
@ -60,6 +60,7 @@ namespace cryptonote
|
||||
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::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;
|
||||
static const command_line::arg_descriptor<std::string> arg_bootstrap_daemon_login;
|
||||
|
@ -241,6 +241,7 @@ struct options {
|
||||
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::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> 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};
|
||||
@ -316,6 +317,7 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl
|
||||
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_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;
|
||||
@ -382,8 +384,11 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl
|
||||
}
|
||||
}
|
||||
|
||||
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), 0, *trusted_daemon, ssl_support, std::make_pair(daemon_ssl_private_key, daemon_ssl_certificate), ssl_allowed_certificates, daemon_ssl_allow_any_cert);
|
||||
wallet->init(std::move(daemon_address), std::move(login), 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);
|
||||
|
||||
boost::filesystem::path ringdb_path = command_line::get_arg(vm, opts.shared_ringdb_dir);
|
||||
wallet->set_ring_database(ringdb_path.string());
|
||||
@ -1044,6 +1049,7 @@ void wallet2::init_options(boost::program_options::options_description& desc_par
|
||||
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_allowed_fingerprints);
|
||||
command_line::add_arg(desc_params, opts.daemon_ssl_allow_any_cert);
|
||||
command_line::add_arg(desc_params, opts.testnet);
|
||||
command_line::add_arg(desc_params, opts.stagenet);
|
||||
@ -1096,7 +1102,7 @@ std::unique_ptr<wallet2> wallet2::make_dummy(const boost::program_options::varia
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
bool wallet2::init(std::string daemon_address, boost::optional<epee::net_utils::http::login> daemon_login, 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, bool allow_any_cert)
|
||||
bool wallet2::init(std::string daemon_address, boost::optional<epee::net_utils::http::login> daemon_login, 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)
|
||||
{
|
||||
m_checkpoints.init_default_checkpoints(m_nettype);
|
||||
if(m_http_client.is_connected())
|
||||
@ -1106,7 +1112,7 @@ bool wallet2::init(std::string daemon_address, boost::optional<epee::net_utils::
|
||||
m_daemon_address = std::move(daemon_address);
|
||||
m_daemon_login = std::move(daemon_login);
|
||||
m_trusted_daemon = trusted_daemon;
|
||||
return m_http_client.set_server(get_daemon_address(), get_daemon_login(), ssl_support, private_key_and_certificate_path, allowed_certificates, allow_any_cert);
|
||||
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);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
bool wallet2::is_deterministic() const
|
||||
|
@ -680,7 +680,8 @@ namespace tools
|
||||
bool trusted_daemon = true,
|
||||
epee::net_utils::ssl_support_t ssl_support = epee::net_utils::ssl_support_t::e_ssl_support_autodetect,
|
||||
const std::pair<std::string, std::string> &private_key_and_certificate_path = {},
|
||||
const std::list<std::string> &allowed_certificates = {}, bool allow_any_cert = false);
|
||||
const std::list<std::string> &allowed_certificates = {}, const std::vector<std::vector<uint8_t>> &allowed_fingerprints = {},
|
||||
bool allow_any_cert = false);
|
||||
|
||||
void stop() { m_run.store(false, std::memory_order_relaxed); m_message_store.stop(); }
|
||||
|
||||
|
@ -67,6 +67,7 @@ namespace
|
||||
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::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";
|
||||
|
||||
@ -240,6 +241,7 @@ namespace tools
|
||||
auto rpc_ssl_private_key = command_line::get_arg(vm, arg_rpc_ssl_private_key);
|
||||
auto rpc_ssl_certificate = command_line::get_arg(vm, arg_rpc_ssl_certificate);
|
||||
auto rpc_ssl_allowed_certificates = command_line::get_arg(vm, arg_rpc_ssl_allowed_certificates);
|
||||
auto rpc_ssl_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))
|
||||
@ -258,11 +260,14 @@ namespace tools
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
m_net_server.set_threads_prefix("RPC");
|
||||
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), allowed_certificates
|
||||
rpc_ssl_support, std::make_pair(rpc_ssl_private_key, rpc_ssl_certificate), std::move(allowed_certificates), std::move(allowed_fingerprints)
|
||||
);
|
||||
}
|
||||
//------------------------------------------------------------------------------------------------------------------------------
|
||||
|
@ -389,6 +389,25 @@ TEST(ToHex, String)
|
||||
|
||||
}
|
||||
|
||||
TEST(FromHex, String)
|
||||
{
|
||||
// the source data to encode and decode
|
||||
std::vector<uint8_t> source{{ 0x00, 0xFF, 0x0F, 0xF0 }};
|
||||
|
||||
// encode and decode the data
|
||||
auto hex = epee::to_hex::string({ source.data(), source.size() });
|
||||
auto decoded = epee::from_hex::vector(hex);
|
||||
|
||||
// encoded should be twice the size and should decode to the exact same data
|
||||
EXPECT_EQ(source.size() * 2, hex.size());
|
||||
EXPECT_EQ(source, decoded);
|
||||
|
||||
// we will now create a padded hex string, we want to explicitly allow
|
||||
// decoding it this way also, ignoring spaces and colons between the numbers
|
||||
hex.assign("00:ff 0f:f0");
|
||||
EXPECT_EQ(source, epee::from_hex::vector(hex));
|
||||
}
|
||||
|
||||
TEST(ToHex, Array)
|
||||
{
|
||||
EXPECT_EQ(
|
||||
|
Loading…
Reference in New Issue
Block a user