rippled
make_SSLContext.cpp
1 //------------------------------------------------------------------------------
2 /*
3  This file is part of rippled: https://github.com/ripple/rippled
4  Copyright (c) 2012, 2013 Ripple Labs Inc.
5 
6  Permission to use, copy, modify, and/or distribute this software for any
7  purpose with or without fee is hereby granted, provided that the above
8  copyright notice and this permission notice appear in all copies.
9 
10  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18 //==============================================================================
19 
20 #include <ripple/basics/chrono.h>
21 #include <ripple/basics/contract.h>
22 #include <ripple/basics/make_SSLContext.h>
23 #include <ripple/beast/container/aged_unordered_set.h>
24 #include <cstdint>
25 #include <sstream>
26 #include <stdexcept>
27 
28 namespace ripple {
29 namespace openssl {
30 namespace detail {
31 
32 // We limit the ciphers we request and allow to ensure that weak
33 // ciphers aren't used. While this isn't strictly necessary for
34 // the rippled server-server use case, where we only need MITM
35 // detection/prevention, we also have websocket and rpc scenarios
36 // and want to ensure weak ciphers can't be used.
38  "HIGH:MEDIUM:!aNULL:!MD5:!DSS:!3DES:!RC4:!EXPORT";
39 
40 template <class>
42 
43 template <>
44 struct custom_delete <RSA>
45 {
46  explicit custom_delete() = default;
47 
48  void operator() (RSA* rsa) const
49  {
50  RSA_free (rsa);
51  }
52 };
53 
54 template <>
55 struct custom_delete <EVP_PKEY>
56 {
57  explicit custom_delete() = default;
58 
59  void operator() (EVP_PKEY* evp_pkey) const
60  {
61  EVP_PKEY_free (evp_pkey);
62  }
63 };
64 
65 template <>
66 struct custom_delete <X509>
67 {
68  explicit custom_delete() = default;
69 
70  void operator() (X509* x509) const
71  {
72  X509_free (x509);
73  }
74 };
75 
76 template <>
77 struct custom_delete <DH>
78 {
79  explicit custom_delete() = default;
80 
81  void operator() (DH* dh) const
82  {
83  DH_free (dh);
84  }
85 };
86 
87 template <class T>
89 
90 // RSA
91 
93 
94 static rsa_ptr rsa_generate_key (int n_bits)
95 {
96 #if OPENSSL_VERSION_NUMBER >= 0x00908000L
97  BIGNUM *bn = BN_new();
98  BN_set_word(bn, RSA_F4);
99 
100  RSA* rsa = RSA_new();
101  if (RSA_generate_key_ex(rsa, n_bits, bn, nullptr) != 1)
102  {
103  RSA_free(rsa);
104  rsa = nullptr;
105  }
106 
107  BN_free(bn);
108 #else
109  RSA* rsa = RSA_generate_key (n_bits, RSA_F4, nullptr, nullptr);
110 #endif
111 
112  if (rsa == nullptr)
113  LogicError ("RSA_generate_key failed");
114 
115  return rsa_ptr (rsa);
116 }
117 
118 // EVP_PKEY
119 
121 
123 {
124  EVP_PKEY* evp_pkey = EVP_PKEY_new();
125 
126  if (evp_pkey == nullptr)
127  LogicError ("EVP_PKEY_new failed");
128 
129  return evp_pkey_ptr (evp_pkey);
130 }
131 
132 static void evp_pkey_assign_rsa (EVP_PKEY* evp_pkey, rsa_ptr rsa)
133 {
134  if (! EVP_PKEY_assign_RSA (evp_pkey, rsa.get()))
135  LogicError ("EVP_PKEY_assign_RSA failed");
136 
137  rsa.release();
138 }
139 
140 // X509
141 
143 
145 {
146  X509* x509 = X509_new();
147 
148  if (x509 == nullptr)
149  LogicError ("X509_new failed");
150 
151  X509_set_version (x509, NID_X509);
152 
153  int const margin = 60 * 60; // 3600, one hour
154  int const length = 10 * 365.25 * 24 * 60 * 60; // 315576000, ten years
155 
156  X509_gmtime_adj (X509_get_notBefore (x509), -margin);
157  X509_gmtime_adj (X509_get_notAfter (x509), length);
158 
159  return x509_ptr (x509);
160 }
161 
162 static void x509_set_pubkey (X509* x509, EVP_PKEY* evp_pkey)
163 {
164  X509_set_pubkey (x509, evp_pkey);
165 }
166 
167 static void x509_sign (X509* x509, EVP_PKEY* evp_pkey)
168 {
169  if (! X509_sign (x509, evp_pkey, EVP_sha1()))
170  LogicError ("X509_sign failed");
171 }
172 
173 static void ssl_ctx_use_certificate (SSL_CTX* const ctx, x509_ptr cert)
174 {
175  if (SSL_CTX_use_certificate (ctx, cert.get()) <= 0)
176  LogicError ("SSL_CTX_use_certificate failed");
177 }
178 
179 static void ssl_ctx_use_privatekey (SSL_CTX* const ctx, evp_pkey_ptr key)
180 {
181  if (SSL_CTX_use_PrivateKey (ctx, key.get()) <= 0)
182  LogicError ("SSL_CTX_use_PrivateKey failed");
183 }
184 
185 #ifdef SSL_FLAGS_NO_RENEGOTIATE_CIPHERS
186 static
187 bool
188 disallowRenegotiation (SSL const* ssl, bool isNew)
189 {
190  // Track when SSL connections have last negotiated and
191  // do not allow a connection to renegotiate more than
192  // once every 4 minutes
193  struct StaticData
194  {
195  std::mutex lock;
197 
198  StaticData()
199  : set (ripple::stopwatch())
200  {
201  }
202  };
203 
204  static StaticData sd;
205  std::lock_guard lock (sd.lock);
206  auto const expired (sd.set.clock().now() - std::chrono::minutes(4));
207 
208  // Remove expired entries
209  for (auto iter (sd.set.chronological.begin ());
210  (iter != sd.set.chronological.end ()) && (iter.when () <= expired);
211  iter = sd.set.chronological.begin ())
212  {
213  sd.set.erase (iter);
214  }
215 
216  auto iter = sd.set.find (ssl);
217  if (iter != sd.set.end ())
218  {
219  if (! isNew)
220  {
221  // This is a renegotiation and the last negotiation was recent
222  return true;
223  }
224 
225  sd.set.touch (iter);
226  }
227  else
228  {
229  sd.set.emplace (ssl);
230  }
231 
232  return false;
233 }
234 
235 static
236 void
237 info_handler (SSL const* ssl, int event, int)
238 {
239 #if OPENSSL_VERSION_NUMBER < 0x10100000L
240  if ((ssl->s3) && (event & SSL_CB_HANDSHAKE_START))
241  {
242  if (disallowRenegotiation (ssl, SSL_in_before (ssl)))
243  ssl->s3->flags |= SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS;
244  }
245 #else
246  // empty, flag removed in OpenSSL 1.1
247 #endif
248 }
249 #endif
250 
251 static
254  boost::system::error_code const& ec)
255 {
257  ss <<
258  what << ": " <<
259  ec.message() <<
260  " (" << ec.value() << ")";
261  return ss.str();
262 }
263 
264 static
265 void
267  boost::asio::ssl::context& context)
268 {
269  using namespace openssl;
270 
271  evp_pkey_ptr pkey = evp_pkey_new();
272  evp_pkey_assign_rsa (pkey.get(), rsa_generate_key (2048));
273 
274  x509_ptr cert = x509_new();
275  x509_set_pubkey (cert.get(), pkey.get());
276  x509_sign (cert.get(), pkey.get());
277 
278  SSL_CTX* const ctx = context.native_handle();
279  ssl_ctx_use_certificate (ctx, std::move(cert));
280  ssl_ctx_use_privatekey (ctx, std::move(pkey));
281 }
282 
283 static
284 void
286  boost::asio::ssl::context& context,
287  std::string const& key_file,
288  std::string const& cert_file,
289  std::string const& chain_file)
290 {
291  SSL_CTX* const ssl = context.native_handle ();
292 
293  bool cert_set = false;
294 
295  if (! cert_file.empty ())
296  {
297  boost::system::error_code ec;
298 
299  context.use_certificate_file (
300  cert_file, boost::asio::ssl::context::pem, ec);
301 
302  if (ec)
303  {
305  "Problem with SSL certificate file.", ec).c_str());
306  }
307 
308  cert_set = true;
309  }
310 
311  if (! chain_file.empty ())
312  {
313  // VFALCO Replace fopen() with RAII
314  FILE* f = fopen (chain_file.c_str (), "r");
315 
316  if (!f)
317  {
319  "Problem opening SSL chain file.", boost::system::error_code (errno,
320  boost::system::generic_category())).c_str());
321  }
322 
323  try
324  {
325  for (;;)
326  {
327  X509* const x = PEM_read_X509 (f, nullptr, nullptr, nullptr);
328 
329  if (x == nullptr)
330  break;
331 
332  if (! cert_set)
333  {
334  if (SSL_CTX_use_certificate (ssl, x) != 1)
335  LogicError ("Problem retrieving SSL certificate from chain file.");
336 
337  cert_set = true;
338  }
339  else if (SSL_CTX_add_extra_chain_cert (ssl, x) != 1)
340  {
341  X509_free (x);
342  LogicError ("Problem adding SSL chain certificate.");
343  }
344  }
345 
346  fclose (f);
347  }
348  catch (std::exception const&)
349  {
350  fclose (f);
351  LogicError ("Reading the SSL chain file generated an exception.");
352  }
353  }
354 
355  if (! key_file.empty ())
356  {
357  boost::system::error_code ec;
358 
359  context.use_private_key_file (key_file,
360  boost::asio::ssl::context::pem, ec);
361 
362  if (ec)
363  {
365  "Problem using the SSL private key file.", ec).c_str());
366  }
367  }
368 
369  if (SSL_CTX_check_private_key (ssl) != 1)
370  {
371  LogicError ("Invalid key in SSL private key file.");
372  }
373 }
374 
376 get_context (std::string const& cipherList)
377 {
378  auto c = std::make_shared<boost::asio::ssl::context> (
379  boost::asio::ssl::context::sslv23);
380 
381  c->set_options (
382  boost::asio::ssl::context::default_workarounds |
383  boost::asio::ssl::context::no_sslv2 |
384  boost::asio::ssl::context::no_sslv3 |
385  boost::asio::ssl::context::single_dh_use);
386 
387  {
388  auto const& l = !cipherList.empty() ? cipherList : defaultCipherList;
389  auto result = SSL_CTX_set_cipher_list (
390  c->native_handle (), l.c_str());
391  if (result != 1)
392  LogicError ("SSL_CTX_set_cipher_list failed");
393  }
394 
395  // These are the raw DH parameters that Ripple Labs has
396  // chosen for Ripple, in the binary format needed by
397  // d2i_DHparams.
398  //
399  unsigned char const params[] =
400  {
401  0x30, 0x82, 0x01, 0x08, 0x02, 0x82, 0x01, 0x01, 0x00, 0x8f, 0xca, 0x66,
402  0x85, 0x33, 0xcb, 0xcf, 0x36, 0x27, 0xb2, 0x4c, 0xb8, 0x50, 0xb8, 0xf9,
403  0x53, 0xf8, 0xb9, 0x2d, 0x1c, 0xa2, 0xad, 0x86, 0x58, 0x29, 0x3b, 0x88,
404  0x3e, 0xf5, 0x65, 0xb8, 0xda, 0x22, 0xf4, 0x8b, 0x21, 0x12, 0x18, 0xf7,
405  0x16, 0xcd, 0x7c, 0xc7, 0x3a, 0x2d, 0x61, 0xb7, 0x11, 0xf6, 0xb0, 0x65,
406  0xa0, 0x5b, 0xa4, 0x06, 0x95, 0x28, 0xa4, 0x4f, 0x76, 0xc0, 0xeb, 0xfa,
407  0x95, 0xdf, 0xbf, 0x19, 0x90, 0x64, 0x8f, 0x60, 0xd5, 0x36, 0xba, 0xab,
408  0x0d, 0x5a, 0x5c, 0x94, 0xd5, 0xf7, 0x32, 0xd6, 0x2a, 0x76, 0x77, 0x83,
409  0x10, 0xc4, 0x2f, 0x10, 0x96, 0x3e, 0x37, 0x84, 0x45, 0x9c, 0xef, 0x33,
410  0xf6, 0xd0, 0x2a, 0xa7, 0xce, 0x0a, 0xce, 0x0d, 0xa1, 0xa7, 0x44, 0x5d,
411  0x18, 0x3f, 0x4f, 0xa4, 0x23, 0x9c, 0x5d, 0x74, 0x4f, 0xee, 0xdf, 0xaa,
412  0x0d, 0x0a, 0x52, 0x57, 0x73, 0xb1, 0xe4, 0xc5, 0x72, 0x93, 0x9d, 0x03,
413  0xe9, 0xf5, 0x48, 0x8c, 0xd1, 0xe6, 0x7c, 0x21, 0x65, 0x4e, 0x16, 0x51,
414  0xa3, 0x16, 0x51, 0x10, 0x75, 0x60, 0x37, 0x93, 0xb8, 0x15, 0xd6, 0x14,
415  0x41, 0x4a, 0x61, 0xc9, 0x1a, 0x4e, 0x9f, 0x38, 0xd8, 0x2c, 0xa5, 0x31,
416  0xe1, 0x87, 0xda, 0x1f, 0xa4, 0x31, 0xa2, 0xa4, 0x42, 0x1e, 0xe0, 0x30,
417  0xea, 0x2f, 0x9b, 0x77, 0x91, 0x59, 0x3e, 0xd5, 0xd0, 0xc5, 0x84, 0x45,
418  0x17, 0x19, 0x74, 0x8b, 0x18, 0xb0, 0xc1, 0xe0, 0xfc, 0x1c, 0xaf, 0xe6,
419  0x2a, 0xef, 0x4e, 0x0e, 0x8a, 0x5c, 0xc2, 0x91, 0xb9, 0x2b, 0xf8, 0x17,
420  0x8d, 0xed, 0x44, 0xaa, 0x47, 0xaa, 0x52, 0xa2, 0xdb, 0xb6, 0xf5, 0xa1,
421  0x88, 0x85, 0xa1, 0xd5, 0x87, 0xb8, 0x07, 0xd3, 0x97, 0xbe, 0x37, 0x74,
422  0x72, 0xf1, 0xa8, 0x29, 0xf1, 0xa7, 0x7d, 0x19, 0xc3, 0x27, 0x09, 0xcf,
423  0x23, 0x02, 0x01, 0x02
424  };
425 
426  unsigned char const *data = &params[0];
427 
428  custom_delete_unique_ptr<DH> const dh {d2i_DHparams (nullptr, &data, sizeof(params))};
429  if (!dh)
430  LogicError ("d2i_DHparams returned nullptr.");
431 
432  SSL_CTX_set_tmp_dh (c->native_handle (), dh.get());
433 
434 #ifdef SSL_FLAGS_NO_RENEGOTIATE_CIPHERS
435  SSL_CTX_set_info_callback (c->native_handle (), info_handler);
436 #endif
437 
438  return c;
439 }
440 
441 } // detail
442 } // openssl
443 
444 //------------------------------------------------------------------------------
446 make_SSLContext(std::string const& cipherList)
447 {
448  auto context = openssl::detail::get_context(cipherList);
450  // VFALCO NOTE, It seems the WebSocket context never has
451  // set_verify_mode called, for either setting of WEBSOCKET_SECURE
452  context->set_verify_mode(boost::asio::ssl::verify_none);
453  return context;
454 }
455 
458  std::string const& keyFile,
459  std::string const& certFile,
460  std::string const& chainFile,
461  std::string const& cipherList)
462 {
463  auto context = openssl::detail::get_context(cipherList);
465  keyFile, certFile, chainFile);
466  return context;
467 }
468 
469 } // ripple
sstream
std::string
STL class.
std::shared_ptr< boost::asio::ssl::context >
std::exception
STL class.
ripple::openssl::detail::get_context
std::shared_ptr< boost::asio::ssl::context > get_context(std::string const &cipherList)
Definition: make_SSLContext.cpp:376
ripple::openssl::detail::ssl_ctx_use_certificate
static void ssl_ctx_use_certificate(SSL_CTX *const ctx, x509_ptr cert)
Definition: make_SSLContext.cpp:173
ripple::make_SSLContext
std::shared_ptr< boost::asio::ssl::context > make_SSLContext(std::string const &cipherList)
Create a self-signed SSL context that allows anonymous Diffie Hellman.
Definition: make_SSLContext.cpp:446
std::chrono::minutes
ripple::openssl::detail::error_message
static std::string error_message(std::string const &what, boost::system::error_code const &ec)
Definition: make_SSLContext.cpp:253
ripple::openssl::detail::ssl_ctx_use_privatekey
static void ssl_ctx_use_privatekey(SSL_CTX *const ctx, evp_pkey_ptr key)
Definition: make_SSLContext.cpp:179
std::stringstream
STL class.
std::unique_ptr::get
T get(T... args)
std::lock_guard
STL class.
ripple::openssl::detail::initAnonymous
static void initAnonymous(boost::asio::ssl::context &context)
Definition: make_SSLContext.cpp:266
std::unique_ptr::release
T release(T... args)
ripple::stopwatch
Stopwatch & stopwatch()
Returns an instance of a wall clock.
Definition: chrono.h:87
ripple::make_SSLContextAuthed
std::shared_ptr< boost::asio::ssl::context > make_SSLContextAuthed(std::string const &keyFile, std::string const &certFile, std::string const &chainFile, std::string const &cipherList)
Create an authenticated SSL context using the specified files.
Definition: make_SSLContext.cpp:457
ripple::openssl::detail::defaultCipherList
const std::string defaultCipherList
Definition: make_SSLContext.cpp:37
stdexcept
ripple::openssl::detail::evp_pkey_assign_rsa
static void evp_pkey_assign_rsa(EVP_PKEY *evp_pkey, rsa_ptr rsa)
Definition: make_SSLContext.cpp:132
ripple::openssl::detail::x509_ptr
custom_delete_unique_ptr< X509 > x509_ptr
Definition: make_SSLContext.cpp:142
ripple::openssl::detail::evp_pkey_ptr
custom_delete_unique_ptr< EVP_PKEY > evp_pkey_ptr
Definition: make_SSLContext.cpp:120
ripple::openssl::detail::custom_delete
Definition: make_SSLContext.cpp:41
std::string::c_str
T c_str(T... args)
ripple::set
bool set(T &target, std::string const &name, Section const &section)
Set a value from a configuration Section If the named value is not found or doesn't parse as a T,...
Definition: BasicConfig.h:271
cstdint
beast::detail::aged_unordered_container
Associative container where each element is also indexed by time.
Definition: aged_unordered_container.h:88
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::openssl::detail::evp_pkey_new
static evp_pkey_ptr evp_pkey_new()
Definition: make_SSLContext.cpp:122
ripple::LogicError
void LogicError(std::string const &how) noexcept
Called when faulty logic causes a broken invariant.
Definition: contract.cpp:50
ripple::openssl::detail::x509_new
static x509_ptr x509_new()
Definition: make_SSLContext.cpp:144
std::string::empty
T empty(T... args)
std::mutex
STL class.
std::stringstream::str
T str(T... args)
ripple::openssl::detail::rsa_ptr
custom_delete_unique_ptr< RSA > rsa_ptr
Definition: make_SSLContext.cpp:92
ripple::openssl::detail::x509_set_pubkey
static void x509_set_pubkey(X509 *x509, EVP_PKEY *evp_pkey)
Definition: make_SSLContext.cpp:162
std::unique_ptr
STL class.
ripple::openssl::detail::initAuthenticated
static void initAuthenticated(boost::asio::ssl::context &context, std::string const &key_file, std::string const &cert_file, std::string const &chain_file)
Definition: make_SSLContext.cpp:285
ripple::openssl::detail::rsa_generate_key
static rsa_ptr rsa_generate_key(int n_bits)
Definition: make_SSLContext.cpp:94
ripple::openssl::detail::x509_sign
static void x509_sign(X509 *x509, EVP_PKEY *evp_pkey)
Definition: make_SSLContext.cpp:167