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 
46 std::string const defaultCipherList = "TLSv1.2:!DSS:!PSK:!eNULL:!aNULL";
47 
48 template <class>
50 
51 template <>
52 struct custom_delete<RSA>
53 {
54  explicit custom_delete() = default;
55 
56  void
57  operator()(RSA* rsa) const
58  {
59  RSA_free(rsa);
60  }
61 };
62 
63 template <>
64 struct custom_delete<EVP_PKEY>
65 {
66  explicit custom_delete() = default;
67 
68  void
69  operator()(EVP_PKEY* evp_pkey) const
70  {
71  EVP_PKEY_free(evp_pkey);
72  }
73 };
74 
75 template <>
76 struct custom_delete<X509>
77 {
78  explicit custom_delete() = default;
79 
80  void
81  operator()(X509* x509) const
82  {
83  X509_free(x509);
84  }
85 };
86 
87 template <>
88 struct custom_delete<DH>
89 {
90  explicit custom_delete() = default;
91 
92  void
93  operator()(DH* dh) const
94  {
95  DH_free(dh);
96  }
97 };
98 
99 template <class T>
101 
102 // RSA
103 
105 
106 static rsa_ptr
107 rsa_generate_key(int n_bits)
108 {
109 #if OPENSSL_VERSION_NUMBER >= 0x00908000L
110  BIGNUM* bn = BN_new();
111  BN_set_word(bn, RSA_F4);
112 
113  RSA* rsa = RSA_new();
114  if (RSA_generate_key_ex(rsa, n_bits, bn, nullptr) != 1)
115  {
116  RSA_free(rsa);
117  rsa = nullptr;
118  }
119 
120  BN_free(bn);
121 #else
122  RSA* rsa = RSA_generate_key(n_bits, RSA_F4, nullptr, nullptr);
123 #endif
124 
125  if (rsa == nullptr)
126  LogicError("RSA_generate_key failed");
127 
128  return rsa_ptr(rsa);
129 }
130 
131 // EVP_PKEY
132 
134 
135 static evp_pkey_ptr
137 {
138  EVP_PKEY* evp_pkey = EVP_PKEY_new();
139 
140  if (evp_pkey == nullptr)
141  LogicError("EVP_PKEY_new failed");
142 
143  return evp_pkey_ptr(evp_pkey);
144 }
145 
146 static void
147 evp_pkey_assign_rsa(EVP_PKEY* evp_pkey, rsa_ptr rsa)
148 {
149  if (!EVP_PKEY_assign_RSA(evp_pkey, rsa.get()))
150  LogicError("EVP_PKEY_assign_RSA failed");
151 
152  rsa.release();
153 }
154 
155 // X509
156 
158 
159 static x509_ptr
161 {
162  X509* x509 = X509_new();
163 
164  if (x509 == nullptr)
165  LogicError("X509_new failed");
166 
167  X509_set_version(x509, NID_X509);
168 
169  int const margin = 60 * 60; // 3600, one hour
170  int const length = 10 * 365.25 * 24 * 60 * 60; // 315576000, ten years
171 
172  X509_gmtime_adj(X509_get_notBefore(x509), -margin);
173  X509_gmtime_adj(X509_get_notAfter(x509), length);
174 
175  return x509_ptr(x509);
176 }
177 
178 static void
179 x509_set_pubkey(X509* x509, EVP_PKEY* evp_pkey)
180 {
181  X509_set_pubkey(x509, evp_pkey);
182 }
183 
184 static void
185 x509_sign(X509* x509, EVP_PKEY* evp_pkey)
186 {
187  if (!X509_sign(x509, evp_pkey, EVP_sha1()))
188  LogicError("X509_sign failed");
189 }
190 
191 static void
192 ssl_ctx_use_certificate(SSL_CTX* const ctx, x509_ptr cert)
193 {
194  if (SSL_CTX_use_certificate(ctx, cert.get()) <= 0)
195  LogicError("SSL_CTX_use_certificate failed");
196 }
197 
198 static void
199 ssl_ctx_use_privatekey(SSL_CTX* const ctx, evp_pkey_ptr key)
200 {
201  if (SSL_CTX_use_PrivateKey(ctx, key.get()) <= 0)
202  LogicError("SSL_CTX_use_PrivateKey failed");
203 }
204 
205 static std::string
206 error_message(std::string const& what, boost::system::error_code const& ec)
207 {
209  ss << what << ": " << ec.message() << " (" << ec.value() << ")";
210  return ss.str();
211 }
212 
213 static void
214 initAnonymous(boost::asio::ssl::context& context)
215 {
216  using namespace openssl;
217 
218  evp_pkey_ptr pkey = evp_pkey_new();
220 
221  x509_ptr cert = x509_new();
222  x509_set_pubkey(cert.get(), pkey.get());
223  x509_sign(cert.get(), pkey.get());
224 
225  SSL_CTX* const ctx = context.native_handle();
226  ssl_ctx_use_certificate(ctx, std::move(cert));
227  ssl_ctx_use_privatekey(ctx, std::move(pkey));
228 }
229 
230 static void
232  boost::asio::ssl::context& context,
233  std::string const& key_file,
234  std::string const& cert_file,
235  std::string const& chain_file)
236 {
237  SSL_CTX* const ssl = context.native_handle();
238 
239  bool cert_set = false;
240 
241  if (!cert_file.empty())
242  {
243  boost::system::error_code ec;
244 
245  context.use_certificate_file(
246  cert_file, boost::asio::ssl::context::pem, ec);
247 
248  if (ec)
249  {
250  LogicError(error_message("Problem with SSL certificate file.", ec)
251  .c_str());
252  }
253 
254  cert_set = true;
255  }
256 
257  if (!chain_file.empty())
258  {
259  // VFALCO Replace fopen() with RAII
260  FILE* f = fopen(chain_file.c_str(), "r");
261 
262  if (!f)
263  {
265  "Problem opening SSL chain file.",
266  boost::system::error_code(
267  errno, boost::system::generic_category()))
268  .c_str());
269  }
270 
271  try
272  {
273  for (;;)
274  {
275  X509* const x = PEM_read_X509(f, nullptr, nullptr, nullptr);
276 
277  if (x == nullptr)
278  break;
279 
280  if (!cert_set)
281  {
282  if (SSL_CTX_use_certificate(ssl, x) != 1)
283  LogicError(
284  "Problem retrieving SSL certificate from chain "
285  "file.");
286 
287  cert_set = true;
288  }
289  else if (SSL_CTX_add_extra_chain_cert(ssl, x) != 1)
290  {
291  X509_free(x);
292  LogicError("Problem adding SSL chain certificate.");
293  }
294  }
295 
296  fclose(f);
297  }
298  catch (std::exception const&)
299  {
300  fclose(f);
301  LogicError("Reading the SSL chain file generated an exception.");
302  }
303  }
304 
305  if (!key_file.empty())
306  {
307  boost::system::error_code ec;
308 
309  context.use_private_key_file(
310  key_file, boost::asio::ssl::context::pem, ec);
311 
312  if (ec)
313  {
314  LogicError(
315  error_message("Problem using the SSL private key file.", ec)
316  .c_str());
317  }
318  }
319 
320  if (SSL_CTX_check_private_key(ssl) != 1)
321  {
322  LogicError("Invalid key in SSL private key file.");
323  }
324 }
325 
327 get_context(std::string const& cipherList)
328 {
329  auto c = std::make_shared<boost::asio::ssl::context>(
330  boost::asio::ssl::context::sslv23);
331 
332  c->set_options(
333  boost::asio::ssl::context::default_workarounds |
334  boost::asio::ssl::context::no_sslv2 |
335  boost::asio::ssl::context::no_sslv3 |
336  boost::asio::ssl::context::no_tlsv1 |
337  boost::asio::ssl::context::no_tlsv1_1 |
338  boost::asio::ssl::context::single_dh_use |
339  boost::asio::ssl::context::no_compression);
340 
341  {
342  auto const& l = !cipherList.empty() ? cipherList : defaultCipherList;
343  auto result = SSL_CTX_set_cipher_list(c->native_handle(), l.c_str());
344  if (result != 1)
345  LogicError("SSL_CTX_set_cipher_list failed");
346  }
347 
348  // These are the raw DH parameters that Ripple Labs has
349  // chosen for Ripple, in the binary format needed by
350  // d2i_DHparams.
351  //
352  unsigned char const params[] = {
353  0x30, 0x82, 0x01, 0x08, 0x02, 0x82, 0x01, 0x01, 0x00, 0x8f, 0xca, 0x66,
354  0x85, 0x33, 0xcb, 0xcf, 0x36, 0x27, 0xb2, 0x4c, 0xb8, 0x50, 0xb8, 0xf9,
355  0x53, 0xf8, 0xb9, 0x2d, 0x1c, 0xa2, 0xad, 0x86, 0x58, 0x29, 0x3b, 0x88,
356  0x3e, 0xf5, 0x65, 0xb8, 0xda, 0x22, 0xf4, 0x8b, 0x21, 0x12, 0x18, 0xf7,
357  0x16, 0xcd, 0x7c, 0xc7, 0x3a, 0x2d, 0x61, 0xb7, 0x11, 0xf6, 0xb0, 0x65,
358  0xa0, 0x5b, 0xa4, 0x06, 0x95, 0x28, 0xa4, 0x4f, 0x76, 0xc0, 0xeb, 0xfa,
359  0x95, 0xdf, 0xbf, 0x19, 0x90, 0x64, 0x8f, 0x60, 0xd5, 0x36, 0xba, 0xab,
360  0x0d, 0x5a, 0x5c, 0x94, 0xd5, 0xf7, 0x32, 0xd6, 0x2a, 0x76, 0x77, 0x83,
361  0x10, 0xc4, 0x2f, 0x10, 0x96, 0x3e, 0x37, 0x84, 0x45, 0x9c, 0xef, 0x33,
362  0xf6, 0xd0, 0x2a, 0xa7, 0xce, 0x0a, 0xce, 0x0d, 0xa1, 0xa7, 0x44, 0x5d,
363  0x18, 0x3f, 0x4f, 0xa4, 0x23, 0x9c, 0x5d, 0x74, 0x4f, 0xee, 0xdf, 0xaa,
364  0x0d, 0x0a, 0x52, 0x57, 0x73, 0xb1, 0xe4, 0xc5, 0x72, 0x93, 0x9d, 0x03,
365  0xe9, 0xf5, 0x48, 0x8c, 0xd1, 0xe6, 0x7c, 0x21, 0x65, 0x4e, 0x16, 0x51,
366  0xa3, 0x16, 0x51, 0x10, 0x75, 0x60, 0x37, 0x93, 0xb8, 0x15, 0xd6, 0x14,
367  0x41, 0x4a, 0x61, 0xc9, 0x1a, 0x4e, 0x9f, 0x38, 0xd8, 0x2c, 0xa5, 0x31,
368  0xe1, 0x87, 0xda, 0x1f, 0xa4, 0x31, 0xa2, 0xa4, 0x42, 0x1e, 0xe0, 0x30,
369  0xea, 0x2f, 0x9b, 0x77, 0x91, 0x59, 0x3e, 0xd5, 0xd0, 0xc5, 0x84, 0x45,
370  0x17, 0x19, 0x74, 0x8b, 0x18, 0xb0, 0xc1, 0xe0, 0xfc, 0x1c, 0xaf, 0xe6,
371  0x2a, 0xef, 0x4e, 0x0e, 0x8a, 0x5c, 0xc2, 0x91, 0xb9, 0x2b, 0xf8, 0x17,
372  0x8d, 0xed, 0x44, 0xaa, 0x47, 0xaa, 0x52, 0xa2, 0xdb, 0xb6, 0xf5, 0xa1,
373  0x88, 0x85, 0xa1, 0xd5, 0x87, 0xb8, 0x07, 0xd3, 0x97, 0xbe, 0x37, 0x74,
374  0x72, 0xf1, 0xa8, 0x29, 0xf1, 0xa7, 0x7d, 0x19, 0xc3, 0x27, 0x09, 0xcf,
375  0x23, 0x02, 0x01, 0x02};
376 
377  unsigned char const* data = &params[0];
378 
380  d2i_DHparams(nullptr, &data, sizeof(params))};
381  if (!dh)
382  LogicError("d2i_DHparams returned nullptr.");
383 
384  SSL_CTX_set_tmp_dh(c->native_handle(), dh.get());
385 
386  // Disable all renegotiation support in TLS v1.2. This can help prevent
387  // exploitation of the bug described in CVE-2021-3499 (for details see
388  // https://www.openssl.org/news/secadv/20210325.txt) when linking against
389  // OpenSSL versions prior to 1.1.1k.
390  SSL_CTX_set_options(c->native_handle(), SSL_OP_NO_RENEGOTIATION);
391 
392  return c;
393 }
394 
395 } // namespace detail
396 } // namespace openssl
397 
398 //------------------------------------------------------------------------------
400 make_SSLContext(std::string const& cipherList)
401 {
402  auto context = openssl::detail::get_context(cipherList);
404  // VFALCO NOTE, It seems the WebSocket context never has
405  // set_verify_mode called, for either setting of WEBSOCKET_SECURE
406  context->set_verify_mode(boost::asio::ssl::verify_none);
407  return context;
408 }
409 
412  std::string const& keyFile,
413  std::string const& certFile,
414  std::string const& chainFile,
415  std::string const& cipherList)
416 {
417  auto context = openssl::detail::get_context(cipherList);
418  openssl::detail::initAuthenticated(*context, keyFile, certFile, chainFile);
419  return context;
420 }
421 
422 } // namespace ripple
sstream
ripple::openssl::detail::custom_delete< RSA >::operator()
void operator()(RSA *rsa) const
Definition: make_SSLContext.cpp:57
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:327
ripple::openssl::detail::evp_pkey_ptr
custom_delete_unique_ptr< EVP_PKEY > evp_pkey_ptr
Definition: make_SSLContext.cpp:133
ripple::openssl::detail::ssl_ctx_use_certificate
static void ssl_ctx_use_certificate(SSL_CTX *const ctx, x509_ptr cert)
Definition: make_SSLContext.cpp:192
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:400
ripple::openssl::detail::error_message
static std::string error_message(std::string const &what, boost::system::error_code const &ec)
Definition: make_SSLContext.cpp:206
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:199
std::stringstream
STL class.
std::unique_ptr::get
T get(T... args)
ripple::openssl::detail::initAnonymous
static void initAnonymous(boost::asio::ssl::context &context)
Definition: make_SSLContext.cpp:214
std::unique_ptr::release
T release(T... args)
ripple::openssl::detail::custom_delete< EVP_PKEY >::operator()
void operator()(EVP_PKEY *evp_pkey) const
Definition: make_SSLContext.cpp:69
ripple::openssl::detail::custom_delete< DH >::operator()
void operator()(DH *dh) const
Definition: make_SSLContext.cpp:93
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:411
ripple::openssl::detail::defaultCipherList
const std::string defaultCipherList
The default list of ciphers we accept over TLS.
Definition: make_SSLContext.cpp:46
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:147
ripple::openssl::detail::custom_delete
Definition: make_SSLContext.cpp:49
std::string::c_str
T c_str(T... args)
cstdint
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:81
ripple::openssl::detail::evp_pkey_new
static evp_pkey_ptr evp_pkey_new()
Definition: make_SSLContext.cpp:136
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:157
ripple::openssl::detail::x509_new
static x509_ptr x509_new()
Definition: make_SSLContext.cpp:160
ripple::openssl::detail::rsa_ptr
custom_delete_unique_ptr< RSA > rsa_ptr
Definition: make_SSLContext.cpp:104
std::string::empty
T empty(T... args)
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:179
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:231
ripple::openssl::detail::rsa_generate_key
static rsa_ptr rsa_generate_key(int n_bits)
Definition: make_SSLContext.cpp:107
ripple::openssl::detail::x509_sign
static void x509_sign(X509 *x509, EVP_PKEY *evp_pkey)
Definition: make_SSLContext.cpp:185