mirror of
https://github.com/Xahau/xahaud.git
synced 2025-11-18 17:45:48 +00:00
Improve self-signed certificate generation:
When starting, the code generates a new ephemeral private key and a corresponding certificate to go along with it. This process can take time and, while this is unlikely to matter for normal server operations, it can have a significant impact for unit testing and development. Profiling data suggests that ~20% of the time needed for a unit test run can be attributed to this. This commit does several things: 1. It restructures the code so that a new self-signed certificate and its corresponding private key are only initialized once at startup; this has minimal impact on the operation of a regular server. 2. It provides new default DH parameters. This doesn't impact the security of the connection, but those who compile from scratch can generate new parameters if they so choose. 3. It properly sets the version number in the certificate, fixing issue #4007; thanks to @donovanhide for the report. 4. It uses SHA-256 instead of SHA-1 as the hash algorithm for the certificate and adds some X.509 extensions as well as a random 128-bit serial number. 5. It rounds the certificate's "start of validity" period so that the server's precise startup time cannot be easily deduced and limits the validity period to two years, down from ten years. 6. It removes some CBC-based ciphers from the default cipher list to avoid some potential security issues, such as CVE-2016-2107 and CVE-2013-0169.
This commit is contained in:
@@ -17,18 +17,55 @@
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <ripple/basics/chrono.h>
|
||||
#include <ripple/basics/contract.h>
|
||||
#include <ripple/basics/make_SSLContext.h>
|
||||
#include <ripple/beast/container/aged_unordered_set.h>
|
||||
#include <cstdint>
|
||||
#include <sstream>
|
||||
#include <ctime>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace ripple {
|
||||
namespace openssl {
|
||||
namespace detail {
|
||||
|
||||
/** The default strength of self-signed RSA certifices.
|
||||
|
||||
Per NIST Special Publication 800-57 Part 3, 2048-bit RSA is still
|
||||
considered acceptably secure. Generally, we would want to go above
|
||||
and beyond such recommendations (e.g. by using 3072 or 4096 bits)
|
||||
but there is a computational cost associated with that may not
|
||||
be worth paying, considering that:
|
||||
|
||||
- We regenerate a new ephemeral certificate and a securely generated
|
||||
random private key every time the server is started; and
|
||||
- There should not be any truly secure information (e.g. seeds or private
|
||||
keys) that gets relayed to the server anyways over these RPCs.
|
||||
|
||||
@note If you increase the number of bits you need to generate new
|
||||
default DH parameters and update defaultDH accordingly.
|
||||
* */
|
||||
int defaultRSAKeyBits = 2048;
|
||||
|
||||
/** The default DH parameters.
|
||||
|
||||
These were generated using the OpenSSL command: `openssl dhparam 2048`
|
||||
by Nik Bougalis <nikb@bougalis.net> on May, 29, 2022.
|
||||
|
||||
It is safe to use this, but if you want you can generate different
|
||||
parameters and put them here. There's no easy way to change this
|
||||
via the config file at this time.
|
||||
|
||||
@note If you increase the number of bits you need to update
|
||||
defaultRSAKeyBits accordingly.
|
||||
*/
|
||||
static constexpr char const defaultDH[] =
|
||||
"-----BEGIN DH PARAMETERS-----\n"
|
||||
"MIIBCAKCAQEApKSWfR7LKy0VoZ/SDCObCvJ5HKX2J93RJ+QN8kJwHh+uuA8G+t8Q\n"
|
||||
"MDRjL5HanlV/sKN9HXqBc7eqHmmbqYwIXKUt9MUZTLNheguddxVlc2IjdP5i9Ps8\n"
|
||||
"l7su8tnP0l1JvC6Rfv3epRsEAw/ZW/lC2IwkQPpOmvnENQhQ6TgrUzcGkv4Bn0X6\n"
|
||||
"pxrDSBpZ+45oehGCUAtcbY8b02vu8zPFoxqo6V/+MIszGzldlik5bVqrJpVF6E8C\n"
|
||||
"tRqHjj6KuDbPbjc+pRGvwx/BSO3SULxmYu9J1NOk090MU1CMt6IJY7TpEc9Xrac9\n"
|
||||
"9yqY3xXZID240RRcaJ25+U4lszFPqP+CEwIBAg==\n"
|
||||
"-----END DH PARAMETERS-----";
|
||||
|
||||
/** The default list of ciphers we accept over TLS.
|
||||
|
||||
Generally we include cipher suites that are part of TLS v1.2, but
|
||||
@@ -43,188 +80,148 @@ namespace detail {
|
||||
global or per-port basis, using the `ssl_ciphers` directive in the
|
||||
config file.
|
||||
*/
|
||||
std::string const defaultCipherList = "TLSv1.2:!DSS:!PSK:!eNULL:!aNULL";
|
||||
|
||||
template <class>
|
||||
struct custom_delete;
|
||||
|
||||
template <>
|
||||
struct custom_delete<RSA>
|
||||
{
|
||||
explicit custom_delete() = default;
|
||||
|
||||
void
|
||||
operator()(RSA* rsa) const
|
||||
{
|
||||
RSA_free(rsa);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct custom_delete<EVP_PKEY>
|
||||
{
|
||||
explicit custom_delete() = default;
|
||||
|
||||
void
|
||||
operator()(EVP_PKEY* evp_pkey) const
|
||||
{
|
||||
EVP_PKEY_free(evp_pkey);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct custom_delete<X509>
|
||||
{
|
||||
explicit custom_delete() = default;
|
||||
|
||||
void
|
||||
operator()(X509* x509) const
|
||||
{
|
||||
X509_free(x509);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct custom_delete<DH>
|
||||
{
|
||||
explicit custom_delete() = default;
|
||||
|
||||
void
|
||||
operator()(DH* dh) const
|
||||
{
|
||||
DH_free(dh);
|
||||
}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
using custom_delete_unique_ptr = std::unique_ptr<T, custom_delete<T>>;
|
||||
|
||||
// RSA
|
||||
|
||||
using rsa_ptr = custom_delete_unique_ptr<RSA>;
|
||||
|
||||
static rsa_ptr
|
||||
rsa_generate_key(int n_bits)
|
||||
{
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x00908000L
|
||||
BIGNUM* bn = BN_new();
|
||||
BN_set_word(bn, RSA_F4);
|
||||
|
||||
RSA* rsa = RSA_new();
|
||||
if (RSA_generate_key_ex(rsa, n_bits, bn, nullptr) != 1)
|
||||
{
|
||||
RSA_free(rsa);
|
||||
rsa = nullptr;
|
||||
}
|
||||
|
||||
BN_free(bn);
|
||||
#else
|
||||
RSA* rsa = RSA_generate_key(n_bits, RSA_F4, nullptr, nullptr);
|
||||
#endif
|
||||
|
||||
if (rsa == nullptr)
|
||||
LogicError("RSA_generate_key failed");
|
||||
|
||||
return rsa_ptr(rsa);
|
||||
}
|
||||
|
||||
// EVP_PKEY
|
||||
|
||||
using evp_pkey_ptr = custom_delete_unique_ptr<EVP_PKEY>;
|
||||
|
||||
static evp_pkey_ptr
|
||||
evp_pkey_new()
|
||||
{
|
||||
EVP_PKEY* evp_pkey = EVP_PKEY_new();
|
||||
|
||||
if (evp_pkey == nullptr)
|
||||
LogicError("EVP_PKEY_new failed");
|
||||
|
||||
return evp_pkey_ptr(evp_pkey);
|
||||
}
|
||||
|
||||
static void
|
||||
evp_pkey_assign_rsa(EVP_PKEY* evp_pkey, rsa_ptr rsa)
|
||||
{
|
||||
if (!EVP_PKEY_assign_RSA(evp_pkey, rsa.get()))
|
||||
LogicError("EVP_PKEY_assign_RSA failed");
|
||||
|
||||
rsa.release();
|
||||
}
|
||||
|
||||
// X509
|
||||
|
||||
using x509_ptr = custom_delete_unique_ptr<X509>;
|
||||
|
||||
static x509_ptr
|
||||
x509_new()
|
||||
{
|
||||
X509* x509 = X509_new();
|
||||
|
||||
if (x509 == nullptr)
|
||||
LogicError("X509_new failed");
|
||||
|
||||
X509_set_version(x509, NID_X509);
|
||||
|
||||
int const margin = 60 * 60; // 3600, one hour
|
||||
int const length = 10 * 365.25 * 24 * 60 * 60; // 315576000, ten years
|
||||
|
||||
X509_gmtime_adj(X509_get_notBefore(x509), -margin);
|
||||
X509_gmtime_adj(X509_get_notAfter(x509), length);
|
||||
|
||||
return x509_ptr(x509);
|
||||
}
|
||||
|
||||
static void
|
||||
x509_set_pubkey(X509* x509, EVP_PKEY* evp_pkey)
|
||||
{
|
||||
X509_set_pubkey(x509, evp_pkey);
|
||||
}
|
||||
|
||||
static void
|
||||
x509_sign(X509* x509, EVP_PKEY* evp_pkey)
|
||||
{
|
||||
if (!X509_sign(x509, evp_pkey, EVP_sha1()))
|
||||
LogicError("X509_sign failed");
|
||||
}
|
||||
|
||||
static void
|
||||
ssl_ctx_use_certificate(SSL_CTX* const ctx, x509_ptr cert)
|
||||
{
|
||||
if (SSL_CTX_use_certificate(ctx, cert.get()) <= 0)
|
||||
LogicError("SSL_CTX_use_certificate failed");
|
||||
}
|
||||
|
||||
static void
|
||||
ssl_ctx_use_privatekey(SSL_CTX* const ctx, evp_pkey_ptr key)
|
||||
{
|
||||
if (SSL_CTX_use_PrivateKey(ctx, key.get()) <= 0)
|
||||
LogicError("SSL_CTX_use_PrivateKey failed");
|
||||
}
|
||||
|
||||
static std::string
|
||||
error_message(std::string const& what, boost::system::error_code const& ec)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << what << ": " << ec.message() << " (" << ec.value() << ")";
|
||||
return ss.str();
|
||||
}
|
||||
std::string const defaultCipherList = "TLSv1.2:!CBC:!DSS:!PSK:!eNULL:!aNULL";
|
||||
|
||||
static void
|
||||
initAnonymous(boost::asio::ssl::context& context)
|
||||
{
|
||||
using namespace openssl;
|
||||
|
||||
evp_pkey_ptr pkey = evp_pkey_new();
|
||||
evp_pkey_assign_rsa(pkey.get(), rsa_generate_key(2048));
|
||||
static auto defaultRSA = []() {
|
||||
BIGNUM* bn = BN_new();
|
||||
BN_set_word(bn, RSA_F4);
|
||||
|
||||
x509_ptr cert = x509_new();
|
||||
x509_set_pubkey(cert.get(), pkey.get());
|
||||
x509_sign(cert.get(), pkey.get());
|
||||
auto rsa = RSA_new();
|
||||
|
||||
if (!rsa)
|
||||
LogicError("RSA_new failed");
|
||||
|
||||
if (RSA_generate_key_ex(rsa, defaultRSAKeyBits, bn, nullptr) != 1)
|
||||
LogicError("RSA_generate_key_ex failure");
|
||||
|
||||
BN_clear_free(bn);
|
||||
|
||||
return rsa;
|
||||
}();
|
||||
|
||||
static auto defaultEphemeralPrivateKey = []() {
|
||||
auto pkey = EVP_PKEY_new();
|
||||
|
||||
if (!pkey)
|
||||
LogicError("EVP_PKEY_new failed");
|
||||
|
||||
// We need to up the reference count of here, since we are retaining a
|
||||
// copy of the key for (potential) reuse.
|
||||
if (RSA_up_ref(defaultRSA) != 1)
|
||||
LogicError(
|
||||
"EVP_PKEY_assign_RSA: incrementing reference count failed");
|
||||
|
||||
if (!EVP_PKEY_assign_RSA(pkey, defaultRSA))
|
||||
LogicError("EVP_PKEY_assign_RSA failed");
|
||||
|
||||
return pkey;
|
||||
}();
|
||||
|
||||
static auto defaultCert = []() {
|
||||
auto x509 = X509_new();
|
||||
|
||||
if (x509 == nullptr)
|
||||
LogicError("X509_new failed");
|
||||
|
||||
// According to the standards (X.509 et al), the value should be one
|
||||
// less than the actualy certificate version we want. Since we want
|
||||
// version 3, we must use a 2.
|
||||
X509_set_version(x509, 2);
|
||||
|
||||
// To avoid leaking information about the precise time that the
|
||||
// server started up, we adjust the validity period:
|
||||
char buf[16] = {0};
|
||||
|
||||
auto const ts = std::time(nullptr) - (25 * 60 * 60);
|
||||
|
||||
int ret = std::strftime(
|
||||
buf, sizeof(buf) - 1, "%y%m%d000000Z", std::gmtime(&ts));
|
||||
|
||||
buf[ret] = 0;
|
||||
|
||||
if (ASN1_TIME_set_string_X509(X509_get_notBefore(x509), buf) != 1)
|
||||
LogicError("Unable to set certificate validity date");
|
||||
|
||||
// And make it valid for two years
|
||||
X509_gmtime_adj(X509_get_notAfter(x509), 2 * 365 * 24 * 60 * 60);
|
||||
|
||||
// Set a serial number
|
||||
if (auto b = BN_new(); b != nullptr)
|
||||
{
|
||||
if (BN_rand(b, 128, BN_RAND_TOP_ANY, BN_RAND_BOTTOM_ANY))
|
||||
{
|
||||
if (auto a = ASN1_INTEGER_new(); a != nullptr)
|
||||
{
|
||||
if (BN_to_ASN1_INTEGER(b, a))
|
||||
X509_set_serialNumber(x509, a);
|
||||
|
||||
ASN1_INTEGER_free(a);
|
||||
}
|
||||
}
|
||||
|
||||
BN_clear_free(b);
|
||||
}
|
||||
|
||||
// Some certificate details
|
||||
{
|
||||
X509V3_CTX ctx;
|
||||
|
||||
X509V3_set_ctx_nodb(&ctx);
|
||||
X509V3_set_ctx(&ctx, x509, x509, nullptr, nullptr, 0);
|
||||
|
||||
if (auto ext = X509V3_EXT_conf_nid(
|
||||
nullptr, &ctx, NID_basic_constraints, "critical,CA:FALSE"))
|
||||
{
|
||||
X509_add_ext(x509, ext, -1);
|
||||
X509_EXTENSION_free(ext);
|
||||
}
|
||||
|
||||
if (auto ext = X509V3_EXT_conf_nid(
|
||||
nullptr,
|
||||
&ctx,
|
||||
NID_ext_key_usage,
|
||||
"critical,serverAuth,clientAuth"))
|
||||
{
|
||||
X509_add_ext(x509, ext, -1);
|
||||
X509_EXTENSION_free(ext);
|
||||
}
|
||||
|
||||
if (auto ext = X509V3_EXT_conf_nid(
|
||||
nullptr, &ctx, NID_key_usage, "critical,digitalSignature"))
|
||||
{
|
||||
X509_add_ext(x509, ext, -1);
|
||||
X509_EXTENSION_free(ext);
|
||||
}
|
||||
|
||||
if (auto ext = X509V3_EXT_conf_nid(
|
||||
nullptr, &ctx, NID_subject_key_identifier, "hash"))
|
||||
{
|
||||
X509_add_ext(x509, ext, -1);
|
||||
X509_EXTENSION_free(ext);
|
||||
}
|
||||
}
|
||||
|
||||
// And a private key
|
||||
X509_set_pubkey(x509, defaultEphemeralPrivateKey);
|
||||
|
||||
if (!X509_sign(x509, defaultEphemeralPrivateKey, EVP_sha256()))
|
||||
LogicError("X509_sign failed");
|
||||
|
||||
return x509;
|
||||
}();
|
||||
|
||||
SSL_CTX* const ctx = context.native_handle();
|
||||
ssl_ctx_use_certificate(ctx, std::move(cert));
|
||||
ssl_ctx_use_privatekey(ctx, std::move(pkey));
|
||||
|
||||
if (SSL_CTX_use_certificate(ctx, defaultCert) <= 0)
|
||||
LogicError("SSL_CTX_use_certificate failed");
|
||||
|
||||
if (SSL_CTX_use_PrivateKey(ctx, defaultEphemeralPrivateKey) <= 0)
|
||||
LogicError("SSL_CTX_use_PrivateKey failed");
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -234,6 +231,10 @@ initAuthenticated(
|
||||
std::string const& cert_file,
|
||||
std::string const& chain_file)
|
||||
{
|
||||
auto fmt_error = [](boost::system::error_code ec) -> std::string {
|
||||
return " [" + std::to_string(ec.value()) + ": " + ec.message() + "]";
|
||||
};
|
||||
|
||||
SSL_CTX* const ssl = context.native_handle();
|
||||
|
||||
bool cert_set = false;
|
||||
@@ -246,10 +247,7 @@ initAuthenticated(
|
||||
cert_file, boost::asio::ssl::context::pem, ec);
|
||||
|
||||
if (ec)
|
||||
{
|
||||
LogicError(error_message("Problem with SSL certificate file.", ec)
|
||||
.c_str());
|
||||
}
|
||||
LogicError("Problem with SSL certificate file" + fmt_error(ec));
|
||||
|
||||
cert_set = true;
|
||||
}
|
||||
@@ -261,11 +259,10 @@ initAuthenticated(
|
||||
|
||||
if (!f)
|
||||
{
|
||||
LogicError(error_message(
|
||||
"Problem opening SSL chain file.",
|
||||
boost::system::error_code(
|
||||
errno, boost::system::generic_category()))
|
||||
.c_str());
|
||||
LogicError(
|
||||
"Problem opening SSL chain file" +
|
||||
fmt_error(boost::system::error_code(
|
||||
errno, boost::system::generic_category())));
|
||||
}
|
||||
|
||||
try
|
||||
@@ -312,8 +309,7 @@ initAuthenticated(
|
||||
if (ec)
|
||||
{
|
||||
LogicError(
|
||||
error_message("Problem using the SSL private key file.", ec)
|
||||
.c_str());
|
||||
"Problem using the SSL private key file" + fmt_error(ec));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -324,7 +320,7 @@ initAuthenticated(
|
||||
}
|
||||
|
||||
std::shared_ptr<boost::asio::ssl::context>
|
||||
get_context(std::string const& cipherList)
|
||||
get_context(std::string cipherList)
|
||||
{
|
||||
auto c = std::make_shared<boost::asio::ssl::context>(
|
||||
boost::asio::ssl::context::sslv23);
|
||||
@@ -338,55 +334,20 @@ get_context(std::string const& cipherList)
|
||||
boost::asio::ssl::context::single_dh_use |
|
||||
boost::asio::ssl::context::no_compression);
|
||||
|
||||
{
|
||||
auto const& l = !cipherList.empty() ? cipherList : defaultCipherList;
|
||||
auto result = SSL_CTX_set_cipher_list(c->native_handle(), l.c_str());
|
||||
if (result != 1)
|
||||
LogicError("SSL_CTX_set_cipher_list failed");
|
||||
}
|
||||
if (cipherList.empty())
|
||||
cipherList = defaultCipherList;
|
||||
|
||||
// These are the raw DH parameters that Ripple Labs has
|
||||
// chosen for Ripple, in the binary format needed by
|
||||
// d2i_DHparams.
|
||||
//
|
||||
unsigned char const params[] = {
|
||||
0x30, 0x82, 0x01, 0x08, 0x02, 0x82, 0x01, 0x01, 0x00, 0x8f, 0xca, 0x66,
|
||||
0x85, 0x33, 0xcb, 0xcf, 0x36, 0x27, 0xb2, 0x4c, 0xb8, 0x50, 0xb8, 0xf9,
|
||||
0x53, 0xf8, 0xb9, 0x2d, 0x1c, 0xa2, 0xad, 0x86, 0x58, 0x29, 0x3b, 0x88,
|
||||
0x3e, 0xf5, 0x65, 0xb8, 0xda, 0x22, 0xf4, 0x8b, 0x21, 0x12, 0x18, 0xf7,
|
||||
0x16, 0xcd, 0x7c, 0xc7, 0x3a, 0x2d, 0x61, 0xb7, 0x11, 0xf6, 0xb0, 0x65,
|
||||
0xa0, 0x5b, 0xa4, 0x06, 0x95, 0x28, 0xa4, 0x4f, 0x76, 0xc0, 0xeb, 0xfa,
|
||||
0x95, 0xdf, 0xbf, 0x19, 0x90, 0x64, 0x8f, 0x60, 0xd5, 0x36, 0xba, 0xab,
|
||||
0x0d, 0x5a, 0x5c, 0x94, 0xd5, 0xf7, 0x32, 0xd6, 0x2a, 0x76, 0x77, 0x83,
|
||||
0x10, 0xc4, 0x2f, 0x10, 0x96, 0x3e, 0x37, 0x84, 0x45, 0x9c, 0xef, 0x33,
|
||||
0xf6, 0xd0, 0x2a, 0xa7, 0xce, 0x0a, 0xce, 0x0d, 0xa1, 0xa7, 0x44, 0x5d,
|
||||
0x18, 0x3f, 0x4f, 0xa4, 0x23, 0x9c, 0x5d, 0x74, 0x4f, 0xee, 0xdf, 0xaa,
|
||||
0x0d, 0x0a, 0x52, 0x57, 0x73, 0xb1, 0xe4, 0xc5, 0x72, 0x93, 0x9d, 0x03,
|
||||
0xe9, 0xf5, 0x48, 0x8c, 0xd1, 0xe6, 0x7c, 0x21, 0x65, 0x4e, 0x16, 0x51,
|
||||
0xa3, 0x16, 0x51, 0x10, 0x75, 0x60, 0x37, 0x93, 0xb8, 0x15, 0xd6, 0x14,
|
||||
0x41, 0x4a, 0x61, 0xc9, 0x1a, 0x4e, 0x9f, 0x38, 0xd8, 0x2c, 0xa5, 0x31,
|
||||
0xe1, 0x87, 0xda, 0x1f, 0xa4, 0x31, 0xa2, 0xa4, 0x42, 0x1e, 0xe0, 0x30,
|
||||
0xea, 0x2f, 0x9b, 0x77, 0x91, 0x59, 0x3e, 0xd5, 0xd0, 0xc5, 0x84, 0x45,
|
||||
0x17, 0x19, 0x74, 0x8b, 0x18, 0xb0, 0xc1, 0xe0, 0xfc, 0x1c, 0xaf, 0xe6,
|
||||
0x2a, 0xef, 0x4e, 0x0e, 0x8a, 0x5c, 0xc2, 0x91, 0xb9, 0x2b, 0xf8, 0x17,
|
||||
0x8d, 0xed, 0x44, 0xaa, 0x47, 0xaa, 0x52, 0xa2, 0xdb, 0xb6, 0xf5, 0xa1,
|
||||
0x88, 0x85, 0xa1, 0xd5, 0x87, 0xb8, 0x07, 0xd3, 0x97, 0xbe, 0x37, 0x74,
|
||||
0x72, 0xf1, 0xa8, 0x29, 0xf1, 0xa7, 0x7d, 0x19, 0xc3, 0x27, 0x09, 0xcf,
|
||||
0x23, 0x02, 0x01, 0x02};
|
||||
if (auto result =
|
||||
SSL_CTX_set_cipher_list(c->native_handle(), cipherList.c_str());
|
||||
result != 1)
|
||||
LogicError("SSL_CTX_set_cipher_list failed");
|
||||
|
||||
unsigned char const* data = ¶ms[0];
|
||||
|
||||
custom_delete_unique_ptr<DH> const dh{
|
||||
d2i_DHparams(nullptr, &data, sizeof(params))};
|
||||
if (!dh)
|
||||
LogicError("d2i_DHparams returned nullptr.");
|
||||
|
||||
SSL_CTX_set_tmp_dh(c->native_handle(), dh.get());
|
||||
c->use_tmp_dh({std::addressof(detail::defaultDH), sizeof(defaultDH)});
|
||||
|
||||
// Disable all renegotiation support in TLS v1.2. This can help prevent
|
||||
// exploitation of the bug described in CVE-2021-3499 (for details see
|
||||
// https://www.openssl.org/news/secadv/20210325.txt) when linking against
|
||||
// OpenSSL versions prior to 1.1.1k.
|
||||
// https://www.openssl.org/news/secadv/20210325.txt) when linking
|
||||
// against OpenSSL versions prior to 1.1.1k.
|
||||
SSL_CTX_set_options(c->native_handle(), SSL_OP_NO_RENEGOTIATION);
|
||||
|
||||
return c;
|
||||
|
||||
Reference in New Issue
Block a user