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