Electroneum
net_ssl.cpp
Go to the documentation of this file.
1 // Copyright (c) 2018, The Monero Project
2 //
3 // All rights reserved.
4 //
5 // Redistribution and use in source and binary forms, with or without modification, are
6 // permitted provided that the following conditions are met:
7 //
8 // 1. Redistributions of source code must retain the above copyright notice, this list of
9 // conditions and the following disclaimer.
10 //
11 // 2. Redistributions in binary form must reproduce the above copyright notice, this list
12 // of conditions and the following disclaimer in the documentation and/or other
13 // materials provided with the distribution.
14 //
15 // 3. Neither the name of the copyright holder nor the names of its contributors may be
16 // used to endorse or promote products derived from this software without specific
17 // prior written permission.
18 //
19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
20 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
21 // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
22 // THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26 // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
27 // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 
29 #include <string.h>
30 #include <boost/asio/ssl.hpp>
31 #include <openssl/ssl.h>
32 #include <openssl/pem.h>
33 #include "misc_log_ex.h"
34 #include "net/net_ssl.h"
35 
36 #undef ELECTRONEUM_DEFAULT_LOG_CATEGORY
37 #define ELECTRONEUM_DEFAULT_LOG_CATEGORY "net.ssl"
38 
39 // openssl genrsa -out /tmp/KEY 4096
40 // openssl req -new -key /tmp/KEY -out /tmp/REQ
41 // openssl x509 -req -days 999999 -sha256 -in /tmp/REQ -signkey /tmp/KEY -out /tmp/CERT
42 
43 namespace
44 {
45  struct openssl_bio_free
46  {
47  void operator()(BIO* ptr) const noexcept
48  {
49  BIO_free(ptr);
50  }
51  };
52  using openssl_bio = std::unique_ptr<BIO, openssl_bio_free>;
53 
54  struct openssl_pkey_free
55  {
56  void operator()(EVP_PKEY* ptr) const noexcept
57  {
58  EVP_PKEY_free(ptr);
59  }
60  };
61  using openssl_pkey = std::unique_ptr<EVP_PKEY, openssl_pkey_free>;
62 
63  struct openssl_rsa_free
64  {
65  void operator()(RSA* ptr) const noexcept
66  {
67  RSA_free(ptr);
68  }
69  };
70  using openssl_rsa = std::unique_ptr<RSA, openssl_rsa_free>;
71 
72  struct openssl_bignum_free
73  {
74  void operator()(BIGNUM* ptr) const noexcept
75  {
76  BN_free(ptr);
77  }
78  };
79  using openssl_bignum = std::unique_ptr<BIGNUM, openssl_bignum_free>;
80 
81  struct openssl_ec_key_free
82  {
83  void operator()(EC_KEY* ptr) const noexcept
84  {
85  EC_KEY_free(ptr);
86  }
87  };
88  using openssl_ec_key = std::unique_ptr<EC_KEY, openssl_ec_key_free>;
89 
90  struct openssl_group_free
91  {
92  void operator()(EC_GROUP* ptr) const noexcept
93  {
94  EC_GROUP_free(ptr);
95  }
96  };
97  using openssl_group = std::unique_ptr<EC_GROUP, openssl_group_free>;
98 
99  boost::system::error_code load_ca_file(boost::asio::ssl::context& ctx, const std::string& path)
100  {
101  SSL_CTX* const ssl_ctx = ctx.native_handle(); // could be moved from context
102  if (ssl_ctx == nullptr)
103  return {boost::asio::error::invalid_argument};
104 
105  if (!SSL_CTX_load_verify_locations(ssl_ctx, path.c_str(), nullptr))
106  {
107  return boost::system::error_code{
108  int(::ERR_get_error()), boost::asio::error::get_ssl_category()
109  };
110  }
111  return boost::system::error_code{};
112  }
113 }
114 
115 namespace epee
116 {
117 namespace net_utils
118 {
119 
120 
121 // https://stackoverflow.com/questions/256405/programmatically-create-x509-certificate-using-openssl
122 bool create_rsa_ssl_certificate(EVP_PKEY *&pkey, X509 *&cert)
123 {
124  MGINFO("Generating SSL certificate");
125  pkey = EVP_PKEY_new();
126  if (!pkey)
127  {
128  MERROR("Failed to create new private key");
129  return false;
130  }
131 
132  openssl_pkey pkey_deleter{pkey};
133  openssl_rsa rsa{RSA_new()};
134  if (!rsa)
135  {
136  MERROR("Error allocating RSA private key");
137  return false;
138  }
139 
140  openssl_bignum exponent{BN_new()};
141  if (!exponent)
142  {
143  MERROR("Error allocating exponent");
144  return false;
145  }
146 
147  BN_set_word(exponent.get(), RSA_F4);
148 
149  if (RSA_generate_key_ex(rsa.get(), 4096, exponent.get(), nullptr) != 1)
150  {
151  MERROR("Error generating RSA private key");
152  return false;
153  }
154 
155  if (EVP_PKEY_assign_RSA(pkey, rsa.get()) <= 0)
156  {
157  MERROR("Error assigning RSA private key");
158  return false;
159  }
160 
161  // the RSA key is now managed by the EVP_PKEY structure
162  (void)rsa.release();
163 
164  cert = X509_new();
165  if (!cert)
166  {
167  MERROR("Failed to create new X509 certificate");
168  return false;
169  }
170  ASN1_INTEGER_set(X509_get_serialNumber(cert), 1);
171  X509_gmtime_adj(X509_get_notBefore(cert), 0);
172  X509_gmtime_adj(X509_get_notAfter(cert), 3600 * 24 * 182); // half a year
173  if (!X509_set_pubkey(cert, pkey))
174  {
175  MERROR("Error setting pubkey on certificate");
176  X509_free(cert);
177  return false;
178  }
179  X509_NAME *name = X509_get_subject_name(cert);
180  X509_set_issuer_name(cert, name);
181 
182  if (X509_sign(cert, pkey, EVP_sha256()) == 0)
183  {
184  MERROR("Error signing certificate");
185  X509_free(cert);
186  return false;
187  }
188  (void)pkey_deleter.release();
189  return true;
190 }
191 
192 bool create_ec_ssl_certificate(EVP_PKEY *&pkey, X509 *&cert, int type)
193 {
194  MGINFO("Generating SSL certificate");
195  pkey = EVP_PKEY_new();
196  if (!pkey)
197  {
198  MERROR("Failed to create new private key");
199  return false;
200  }
201 
202  openssl_pkey pkey_deleter{pkey};
203  openssl_ec_key ec_key{EC_KEY_new()};
204  if (!ec_key)
205  {
206  MERROR("Error allocating EC private key");
207  return false;
208  }
209 
210  EC_GROUP *group = EC_GROUP_new_by_curve_name(type);
211  if (!group)
212  {
213  MERROR("Error getting EC group " << type);
214  return false;
215  }
216  openssl_group group_deleter{group};
217 
218  EC_GROUP_set_asn1_flag(group, OPENSSL_EC_NAMED_CURVE);
219  EC_GROUP_set_point_conversion_form(group, POINT_CONVERSION_UNCOMPRESSED);
220 
221  if (!EC_GROUP_check(group, NULL))
222  {
223  MERROR("Group failed check: " << ERR_reason_error_string(ERR_get_error()));
224  return false;
225  }
226  if (EC_KEY_set_group(ec_key.get(), group) != 1)
227  {
228  MERROR("Error setting EC group");
229  return false;
230  }
231  if (EC_KEY_generate_key(ec_key.get()) != 1)
232  {
233  MERROR("Error generating EC private key");
234  return false;
235  }
236  if (EVP_PKEY_assign_EC_KEY(pkey, ec_key.get()) <= 0)
237  {
238  MERROR("Error assigning EC private key");
239  return false;
240  }
241 
242  // the key is now managed by the EVP_PKEY structure
243  (void)ec_key.release();
244 
245  cert = X509_new();
246  if (!cert)
247  {
248  MERROR("Failed to create new X509 certificate");
249  return false;
250  }
251  ASN1_INTEGER_set(X509_get_serialNumber(cert), 1);
252  X509_gmtime_adj(X509_get_notBefore(cert), 0);
253  X509_gmtime_adj(X509_get_notAfter(cert), 3600 * 24 * 182); // half a year
254  if (!X509_set_pubkey(cert, pkey))
255  {
256  MERROR("Error setting pubkey on certificate");
257  X509_free(cert);
258  return false;
259  }
260  X509_NAME *name = X509_get_subject_name(cert);
261  X509_set_issuer_name(cert, name);
262 
263  if (X509_sign(cert, pkey, EVP_sha256()) == 0)
264  {
265  MERROR("Error signing certificate");
266  X509_free(cert);
267  return false;
268  }
269  (void)pkey_deleter.release();
270  return true;
271 }
272 
273 ssl_options_t::ssl_options_t(std::vector<std::vector<std::uint8_t>> fingerprints, std::string ca_path)
274  : fingerprints_(std::move(fingerprints)),
275  ca_path(std::move(ca_path)),
276  auth(),
279 {
280  std::sort(fingerprints_.begin(), fingerprints_.end());
281 }
282 
284 {
285  boost::asio::ssl::context ssl_context{boost::asio::ssl::context::tlsv12};
286  if (!bool(*this))
287  return ssl_context;
288 
289  // only allow tls v1.2 and up
290  ssl_context.set_options(boost::asio::ssl::context::default_workarounds);
291  ssl_context.set_options(boost::asio::ssl::context::no_sslv2);
292  ssl_context.set_options(boost::asio::ssl::context::no_sslv3);
293  ssl_context.set_options(boost::asio::ssl::context::no_tlsv1);
294  ssl_context.set_options(boost::asio::ssl::context::no_tlsv1_1);
295 
296  // only allow a select handful of tls v1.3 and v1.2 ciphers to be used
297  SSL_CTX_set_cipher_list(ssl_context.native_handle(), "ECDHE-ECDSA-CHACHA20-POLY1305-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256");
298 
299  // set options on the SSL context for added security
300  SSL_CTX *ctx = ssl_context.native_handle();
301  CHECK_AND_ASSERT_THROW_MES(ctx, "Failed to get SSL context");
302  SSL_CTX_clear_options(ctx, SSL_OP_LEGACY_SERVER_CONNECT); // SSL_CTX_SET_OPTIONS(3)
303  SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF); // https://stackoverflow.com/questions/22378442
304 #ifdef SSL_OP_NO_TICKET
305  SSL_CTX_set_options(ctx, SSL_OP_NO_TICKET); // https://stackoverflow.com/questions/22378442
306 #endif
307 #ifdef SSL_OP_NO_RENEGOTIATION
308  SSL_CTX_set_options(ctx, SSL_OP_NO_RENEGOTIATION);
309 #endif
310 #ifdef SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION
311  SSL_CTX_set_options(ctx, SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
312 #endif
313 #ifdef SSL_OP_NO_COMPRESSION
314  SSL_CTX_set_options(ctx, SSL_OP_NO_COMPRESSION);
315 #endif
316 #ifdef SSL_OP_CIPHER_SERVER_PREFERENCE
317  SSL_CTX_set_options(ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
318 #endif
319  SSL_CTX_set_ecdh_auto(ctx, 1);
320 
321  switch (verification)
322  {
324  ssl_context.set_default_verify_paths();
325  break;
327  ssl_context.set_verify_depth(0);
328  /* fallthrough */
330  if (!ca_path.empty())
331  {
332  const boost::system::error_code err = load_ca_file(ssl_context, ca_path);
333  if (err)
334  throw boost::system::system_error{err, "Failed to load user CA file at " + ca_path};
335  }
336  break;
337  default:
338  break;
339  }
340 
341  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");
342  if (auth.private_key_path.empty())
343  {
344  EVP_PKEY *pkey;
345  X509 *cert;
346  bool ok = false;
347 
348 #ifdef USE_EXTRA_EC_CERT
349  CHECK_AND_ASSERT_THROW_MES(create_ec_ssl_certificate(pkey, cert, NID_secp256k1), "Failed to create certificate");
350  CHECK_AND_ASSERT_THROW_MES(SSL_CTX_use_certificate(ctx, cert), "Failed to use generated certificate");
351  if (!SSL_CTX_use_PrivateKey(ctx, pkey))
352  MERROR("Failed to use generated EC private key for " << NID_secp256k1);
353  else
354  ok = true;
355  X509_free(cert);
356  EVP_PKEY_free(pkey);
357 #endif
358 
359  CHECK_AND_ASSERT_THROW_MES(create_rsa_ssl_certificate(pkey, cert), "Failed to create certificate");
360  CHECK_AND_ASSERT_THROW_MES(SSL_CTX_use_certificate(ctx, cert), "Failed to use generated certificate");
361  if (!SSL_CTX_use_PrivateKey(ctx, pkey))
362  MERROR("Failed to use generated RSA private key for RSA");
363  else
364  ok = true;
365  X509_free(cert);
366  EVP_PKEY_free(pkey);
367 
368  CHECK_AND_ASSERT_THROW_MES(ok, "Failed to use any generated certificate");
369  }
370  else
371  auth.use_ssl_certificate(ssl_context);
372 
373  return ssl_context;
374 }
375 
377 {
378  ssl_context.use_private_key_file(private_key_path, boost::asio::ssl::context::pem);
379  ssl_context.use_certificate_chain_file(certificate_path);
380 }
381 
382 bool is_ssl(const unsigned char *data, size_t len)
383 {
384  if (len < get_ssl_magic_size())
385  return false;
386 
387  // https://security.stackexchange.com/questions/34780/checking-client-hello-for-https-classification
388  MDEBUG("SSL detection buffer, " << len << " bytes: "
389  << (unsigned)(unsigned char)data[0] << " " << (unsigned)(unsigned char)data[1] << " "
390  << (unsigned)(unsigned char)data[2] << " " << (unsigned)(unsigned char)data[3] << " "
391  << (unsigned)(unsigned char)data[4] << " " << (unsigned)(unsigned char)data[5] << " "
392  << (unsigned)(unsigned char)data[6] << " " << (unsigned)(unsigned char)data[7] << " "
393  << (unsigned)(unsigned char)data[8]);
394  if (data[0] == 0x16) // record
395  if (data[1] == 3) // major version
396  if (data[5] == 1) // ClientHello
397  if (data[6] == 0 && data[3]*256 + data[4] == data[7]*256 + data[8] + 4) // length check
398  return true;
399  return false;
400 }
401 
402 bool ssl_options_t::has_strong_verification(boost::string_ref host) const noexcept
403 {
404  // onion and i2p addresses contain information about the server cert
405  // which both authenticates and encrypts
406  if (host.ends_with(".onion") || host.ends_with(".i2p"))
407  return true;
408  switch (verification)
409  {
410  default:
413  return false;
416  break;
417  }
418  return true;
419 }
420 
421 bool ssl_options_t::has_fingerprint(boost::asio::ssl::verify_context &ctx) const
422 {
423  // can we check the certificate against a list of fingerprints?
424  if (!fingerprints_.empty()) {
425  X509_STORE_CTX *sctx = ctx.native_handle();
426  if (!sctx)
427  {
428  MERROR("Error getting verify_context handle");
429  return false;
430  }
431 
432  X509* cert = nullptr;
433  const STACK_OF(X509)* chain = X509_STORE_CTX_get_chain(sctx);
434  if (!chain || sk_X509_num(chain) < 1 || !(cert = sk_X509_value(chain, 0)))
435  {
436  MERROR("No certificate found in verify_context");
437  return false;
438  }
439 
440  // buffer for the certificate digest and the size of the result
441  std::vector<uint8_t> digest(EVP_MAX_MD_SIZE);
442  unsigned int size{ 0 };
443 
444  // create the digest from the certificate
445  if (!X509_digest(cert, EVP_sha256(), digest.data(), &size)) {
446  MERROR("Failed to create certificate fingerprint");
447  return false;
448  }
449 
450  // strip unnecessary bytes from the digest
451  digest.resize(size);
452 
453  return std::binary_search(fingerprints_.begin(), fingerprints_.end(), digest);
454  }
455 
456  return false;
457 }
458 
459 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
460 {
461  socket.next_layer().set_option(boost::asio::ip::tcp::no_delay(true));
462 
463  /* Using system-wide CA store for client verification is funky - there is
464  no expected hostname for server to verify against. If server doesn't have
465  specific whitelisted certificates for client, don't require client to
466  send certificate at all. */
467  const bool no_verification = verification == ssl_verification_t::none ||
468  (type == boost::asio::ssl::stream_base::server && fingerprints_.empty() && ca_path.empty());
469 
470  /* According to OpenSSL documentation (and SSL specifications), server must
471  always send certificate unless "anonymous" cipher mode is used which are
472  disabled by default. Either way, the certificate is never inspected. */
473  if (no_verification)
474  socket.set_verify_mode(boost::asio::ssl::verify_none);
475  else
476  {
477  socket.set_verify_mode(boost::asio::ssl::verify_peer | boost::asio::ssl::verify_fail_if_no_peer_cert);
478 
479  // in case server is doing "virtual" domains, set hostname
480  SSL* const ssl_ctx = socket.native_handle();
481  if (type == boost::asio::ssl::stream_base::client && !host.empty() && ssl_ctx)
482  SSL_set_tlsext_host_name(ssl_ctx, host.c_str());
483 
484  socket.set_verify_callback([&](const bool preverified, boost::asio::ssl::verify_context &ctx)
485  {
486  // preverified means it passed system or user CA check. System CA is never loaded
487  // when fingerprints are whitelisted.
488  const bool verified = preverified &&
489  (verification != ssl_verification_t::system_ca || host.empty() || boost::asio::ssl::rfc2818_verification(host)(preverified, ctx));
490 
491  if (!verified && !has_fingerprint(ctx))
492  {
493  // autodetect will reconnect without SSL - warn and keep connection encrypted
495  {
496  MERROR("SSL certificate is not in the allowed list, connection droppped");
497  return false;
498  }
499  MWARNING("SSL peer has not been verified");
500  }
501  return true;
502  });
503  }
504 
505  boost::system::error_code ec;
506  socket.handshake(type, ec);
507  if (ec)
508  {
509  MERROR("SSL handshake failed, connection dropped: " << ec.message());
510  return false;
511  }
512  MDEBUG("SSL handshake success");
513  return true;
514 }
515 
516 bool ssl_support_from_string(ssl_support_t &ssl, boost::string_ref s)
517 {
518  if (s == "enabled")
520  else if (s == "disabled")
522  else if (s == "autodetect")
524  else
525  return false;
526  return true;
527 }
528 
529 } // namespace
530 } // namespace
531 
#define MERROR(x)
Definition: misc_log_ex.h:73
#define CHECK_AND_ASSERT_THROW_MES(expr, message)
Definition: misc_log_ex.h:173
bool ssl_support_from_string(ssl_support_t &ssl, boost::string_ref s)
Definition: net_ssl.cpp:516
constexpr size_t get_ssl_magic_size()
Definition: net_ssl.h:135
ssl_options_t(ssl_support_t support)
Verification is set to system ca unless SSL is disabled.
Definition: net_ssl.h:85
bool has_fingerprint(boost::asio::ssl::verify_context &ctx) const
Search against internal fingerprints. Always false if behavior() != user_certificate_check.
Definition: net_ssl.cpp:421
::std::string string
Definition: gtest-port.h:1097
bool create_rsa_ssl_certificate(EVP_PKEY *&pkey, X509 *&cert)
Definition: net_ssl.cpp:122
std::unique_ptr< void, close > socket
Unique ZMQ socket handle, calls zmq_close on destruction.
Definition: zmq.h:101
STL namespace.
void use_ssl_certificate(boost::asio::ssl::context &ssl_context) const
Load private_key_path and certificate_path into ssl_context.
Definition: net_ssl.cpp:376
std::string certificate_path
Certificate used for authentication to peer.
Definition: net_ssl.h:63
const char * name
#define MGINFO(x)
Definition: misc_log_ex.h:80
#define MDEBUG(x)
Definition: misc_log_ex.h:76
Verify peer via system ca only (do not inspect user certificates)
bool is_ssl(const unsigned char *data, size_t len)
Definition: net_ssl.cpp:382
Verify peer via specific (possibly chain) certificate(s) only.
ssl_authentication_t auth
Definition: net_ssl.h:80
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
Definition: net_ssl.cpp:459
std::unique_ptr< void, terminate > context
Unique ZMQ context handle, calls zmq_term on destruction.
Definition: zmq.h:98
Verify peer via specific (non-chain) certificate(s) only.
bool create_ec_ssl_certificate(EVP_PKEY *&pkey, X509 *&cert)
#define MWARNING(x)
Definition: misc_log_ex.h:74
const T & move(const T &t)
Definition: gtest-port.h:1317
ssl_verification_t verification
Definition: net_ssl.h:82
bool has_strong_verification(boost::string_ref host) const noexcept
True if host can be verified using this configuration WITHOUT system "root" CAs.
Definition: net_ssl.cpp:402
std::string private_key_path
Private key used for authentication.
Definition: net_ssl.h:62
boost::asio::ssl::context create_context() const
Definition: net_ssl.cpp:283