Electroneum
rpc_args.cpp
Go to the documentation of this file.
1 // Copyrights(c) 2017-2021, The Electroneum Project
2 // Copyrights(c) 2014-2019, The Monero Project
3 //
4 // All rights reserved.
5 //
6 // Redistribution and use in source and binary forms, with or without modification, are
7 // permitted provided that the following conditions are met:
8 //
9 // 1. Redistributions of source code must retain the above copyright notice, this list of
10 // conditions and the following disclaimer.
11 //
12 // 2. Redistributions in binary form must reproduce the above copyright notice, this list
13 // of conditions and the following disclaimer in the documentation and/or other
14 // materials provided with the distribution.
15 //
16 // 3. Neither the name of the copyright holder nor the names of its contributors may be
17 // used to endorse or promote products derived from this software without specific
18 // prior written permission.
19 //
20 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
21 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
22 // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
23 // THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
27 // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
28 // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 //
30 #include "rpc_args.h"
31 
32 #include <boost/algorithm/string.hpp>
33 #include <boost/asio/ip/address.hpp>
34 #include <functional>
35 #include "common/command_line.h"
36 #include "common/i18n.h"
37 #include "hex.h"
38 
39 namespace cryptonote
40 {
41  namespace
42  {
43  boost::optional<epee::net_utils::ssl_options_t> do_process_ssl(const boost::program_options::variables_map& vm, const rpc_args::descriptors& arg, const bool any_cert_option)
44  {
45  bool ssl_required = false;
47  if (any_cert_option && command_line::get_arg(vm, arg.rpc_ssl_allow_any_cert))
49  else
50  {
51  std::string ssl_ca_file = command_line::get_arg(vm, arg.rpc_ssl_ca_certificates);
52  const std::vector<std::string> ssl_allowed_fingerprints = command_line::get_arg(vm, arg.rpc_ssl_allowed_fingerprints);
53 
54  std::vector<std::vector<uint8_t>> allowed_fingerprints{ ssl_allowed_fingerprints.size() };
55  std::transform(ssl_allowed_fingerprints.begin(), ssl_allowed_fingerprints.end(), allowed_fingerprints.begin(), epee::from_hex::vector);
56  for (const auto &fpr: allowed_fingerprints)
57  {
58  if (fpr.size() != SSL_FINGERPRINT_SIZE)
59  {
60  MERROR("SHA-256 fingerprint should be " BOOST_PP_STRINGIZE(SSL_FINGERPRINT_SIZE) " bytes long.");
61  return boost::none;
62  }
63  }
64 
65  if (!allowed_fingerprints.empty() || !ssl_ca_file.empty())
66  {
67  ssl_required = true;
68  ssl_options = epee::net_utils::ssl_options_t{
69  std::move(allowed_fingerprints), std::move(ssl_ca_file)
70  };
71 
72  if (command_line::get_arg(vm, arg.rpc_ssl_allow_chained))
74  }
75  }
76 
77  // user specified CA file or fingeprints implies enabled SSL by default
78  if (!ssl_required && !epee::net_utils::ssl_support_from_string(ssl_options.support, command_line::get_arg(vm, arg.rpc_ssl)))
79  {
80  MERROR("Invalid argument for " << std::string(arg.rpc_ssl.name));
81  return boost::none;
82  }
83 
85  command_line::get_arg(vm, arg.rpc_ssl_private_key), command_line::get_arg(vm, arg.rpc_ssl_certificate)
86  };
87 
88  return {std::move(ssl_options)};
89  }
90  } // anonymous
91 
93  : rpc_bind_ip({"rpc-bind-ip", rpc_args::tr("Specify IP to bind RPC server"), "127.0.0.1"})
94  , rpc_login({"rpc-login", rpc_args::tr("Specify username[:password] required for RPC server"), "", true})
95  , confirm_external_bind({"confirm-external-bind", rpc_args::tr("Confirm rpc-bind-ip value is NOT a loopback (local) IP")})
96  , rpc_access_control_origins({"rpc-access-control-origins", rpc_args::tr("Specify a comma separated list of origins to allow cross origin resource sharing"), ""})
97  , rpc_ssl({"rpc-ssl", rpc_args::tr("Enable SSL on RPC connections: enabled|disabled|autodetect"), "autodetect"})
98  , rpc_ssl_private_key({"rpc-ssl-private-key", rpc_args::tr("Path to a PEM format private key"), ""})
99  , rpc_ssl_certificate({"rpc-ssl-certificate", rpc_args::tr("Path to a PEM format certificate"), ""})
100  , rpc_ssl_ca_certificates({"rpc-ssl-ca-certificates", rpc_args::tr("Path to file containing concatenated PEM format certificate(s) to replace system CA(s)."), ""})
101  , rpc_ssl_allowed_fingerprints({"rpc-ssl-allowed-fingerprints", rpc_args::tr("List of certificate fingerprints to allow")})
102  , rpc_ssl_allow_chained({"rpc-ssl-allow-chained", rpc_args::tr("Allow user (via --rpc-ssl-certificates) chain certificates"), false})
103  , rpc_ssl_allow_any_cert({"rpc-ssl-allow-any-cert", rpc_args::tr("Allow any peer certificate"), false})
104  {}
105 
106  const char* rpc_args::tr(const char* str) { return i18n_translate(str, "cryptonote::rpc_args"); }
107 
108  void rpc_args::init_options(boost::program_options::options_description& desc, const bool any_cert_option)
109  {
110  const descriptors arg{};
111  command_line::add_arg(desc, arg.rpc_bind_ip);
112  command_line::add_arg(desc, arg.rpc_login);
113  command_line::add_arg(desc, arg.confirm_external_bind);
114  command_line::add_arg(desc, arg.rpc_access_control_origins);
115  command_line::add_arg(desc, arg.rpc_ssl);
116  command_line::add_arg(desc, arg.rpc_ssl_private_key);
117  command_line::add_arg(desc, arg.rpc_ssl_certificate);
118  command_line::add_arg(desc, arg.rpc_ssl_ca_certificates);
119  command_line::add_arg(desc, arg.rpc_ssl_allowed_fingerprints);
120  command_line::add_arg(desc, arg.rpc_ssl_allow_chained);
121  if (any_cert_option)
122  command_line::add_arg(desc, arg.rpc_ssl_allow_any_cert);
123  }
124 
125  boost::optional<rpc_args> rpc_args::process(const boost::program_options::variables_map& vm, const bool any_cert_option)
126  {
127  const descriptors arg{};
128  rpc_args config{};
129 
130  config.bind_ip = command_line::get_arg(vm, arg.rpc_bind_ip);
131  if (!config.bind_ip.empty())
132  {
133  // always parse IP here for error consistency
134  boost::system::error_code ec{};
135  const auto parsed_ip = boost::asio::ip::address::from_string(config.bind_ip, ec);
136  if (ec)
137  {
138  LOG_ERROR(tr("Invalid IP address given for --") << arg.rpc_bind_ip.name);
139  return boost::none;
140  }
141 
142  if (!parsed_ip.is_loopback() && !command_line::get_arg(vm, arg.confirm_external_bind))
143  {
144  LOG_ERROR(
145  "--" << arg.rpc_bind_ip.name <<
146  tr(" permits inbound unencrypted external connections. Consider SSH tunnel or SSL proxy instead. Override with --") <<
147  arg.confirm_external_bind.name
148  );
149  return boost::none;
150  }
151  }
152 
153  const char *env_rpc_login = nullptr;
154  const bool has_rpc_arg = command_line::has_arg(vm, arg.rpc_login);
155  const bool use_rpc_env = !has_rpc_arg && (env_rpc_login = getenv("RPC_LOGIN")) != nullptr && strlen(env_rpc_login) > 0;
156  boost::optional<tools::login> login{};
157  if (has_rpc_arg || use_rpc_env)
158  {
159  config.login = tools::login::parse(
160  has_rpc_arg ? command_line::get_arg(vm, arg.rpc_login) : std::string(env_rpc_login), true, [](bool verify) {
161  return tools::password_container::prompt(verify, "RPC server password");
162  });
163 
164  if (!config.login)
165  return boost::none;
166 
167  if (config.login->username.empty())
168  {
169  LOG_ERROR(tr("Username specified with --") << arg.rpc_login.name << tr(" cannot be empty"));
170  return boost::none;
171  }
172  }
173 
174  auto access_control_origins_input = command_line::get_arg(vm, arg.rpc_access_control_origins);
175  if (!access_control_origins_input.empty())
176  {
177  if (!config.login)
178  {
179  LOG_ERROR(arg.rpc_access_control_origins.name << tr(" requires RPC server password --") << arg.rpc_login.name << tr(" cannot be empty"));
180  return boost::none;
181  }
182 
183  std::vector<std::string> access_control_origins;
184  boost::split(access_control_origins, access_control_origins_input, boost::is_any_of(","));
185  std::for_each(access_control_origins.begin(), access_control_origins.end(), std::bind(&boost::trim<std::string>, std::placeholders::_1, std::locale::classic()));
186  config.access_control_origins = std::move(access_control_origins);
187  }
188 
189  auto ssl_options = do_process_ssl(vm, arg, any_cert_option);
190  if (!ssl_options)
191  return boost::none;
192  config.ssl_options = std::move(*ssl_options);
193 
194  return {std::move(config)};
195  }
196 
197  boost::optional<epee::net_utils::ssl_options_t> rpc_args::process_ssl(const boost::program_options::variables_map& vm, const bool any_cert_option)
198  {
199  const descriptors arg{};
200  return do_process_ssl(vm, arg, any_cert_option);
201  }
202 }
#define MERROR(x)
Definition: misc_log_ex.h:73
bool ssl_support_from_string(ssl_support_t &ssl, boost::string_ref s)
Definition: net_ssl.cpp:516
::std::string string
Definition: gtest-port.h:1097
std::vector< std::string > access_control_origins
Definition: rpc_args.h:80
static boost::optional< rpc_args > process(const boost::program_options::variables_map &vm, const bool any_cert_option=false)
Definition: rpc_args.cpp:125
static const char * tr(const char *str)
Definition: rpc_args.cpp:106
const char * i18n_translate(const char *s, const std::string &context)
Definition: i18n.cpp:323
static void init_options(boost::program_options::options_description &desc, const bool any_cert_option=false)
Definition: rpc_args.cpp:108
Holds cryptonote related classes and helpers.
Definition: ban.cpp:40
std::enable_if<!std::is_same< T, bool >::value, bool >::type has_arg(const boost::program_options::variables_map &vm, const arg_descriptor< T, required, dependent, NUM_DEPS > &arg)
Definition: command_line.h:258
static std::vector< uint8_t > vector(boost::string_ref src)
Definition: hex.cpp:88
static boost::optional< epee::net_utils::ssl_options_t > process_ssl(const boost::program_options::variables_map &vm, const bool any_cert_option=false)
Definition: rpc_args.cpp:197
Verify peer via specific (possibly chain) certificate(s) only.
ssl_authentication_t auth
Definition: net_ssl.h:80
#define SSL_FINGERPRINT_SIZE
Definition: net_ssl.h:40
epee::net_utils::ssl_options_t ssl_options
Definition: rpc_args.h:82
void add_arg(boost::program_options::options_description &description, const arg_descriptor< T, required, dependent, NUM_DEPS > &arg, bool unique=true)
Definition: command_line.h:188
Processes command line arguments related to server-side RPC.
Definition: rpc_args.h:44
static boost::optional< login > parse(std::string &&userpass, bool verify, const std::function< boost::optional< password_container >(bool)> &prompt)
Definition: password.cpp:268
const T & move(const T &t)
Definition: gtest-port.h:1317
#define LOG_ERROR(x)
Definition: misc_log_ex.h:98
boost::optional< tools::login > login
Definition: rpc_args.h:81
T get_arg(const boost::program_options::variables_map &vm, const arg_descriptor< T, false, true > &arg)
Definition: command_line.h:271
ssl_verification_t verification
Definition: net_ssl.h:82