openssl/bio.h \
openssl/bn.h \
openssl/crypto.h \
+ openssl/decoder.h \
openssl/dh.h \
openssl/err.h \
openssl/evp.h \
<sect1>Changes to existing directives<label id="modifieddirectives">
<p>
<descrip>
- <p>There have been no directives changed.
+ <tag>ssl_engine</tag>
+ <p>OpenSSL 3.0.0 deprecates the Engine feature. This directive is
+ only supported when Squid is built for older OpenSSL versions.
</descrip>
DOC_START
The OpenSSL engine to use. You will need to set this if you
would like to use hardware SSL acceleration for example.
+
+ Not supported in builds with OpenSSL v3 or newer.
DOC_END
NAME: sslproxy_session_ttl
printf("%s\n",SQUID_BUILD_INFO);
#if USE_OPENSSL
printf("\nThis binary uses %s. ", OpenSSL_version(OPENSSL_VERSION));
+#if OPENSSL_VERSION_MAJOR < 3
printf("For legal restrictions on distribution see https://www.openssl.org/source/license.html\n\n");
+#endif
#endif
printf( "configure options: %s\n", SQUID_CONFIGURE_OPTIONS);
/// set of options we can parse and what they map to
static struct ssl_option {
const char *name;
- long value;
+ Security::ParsedOptions value;
} ssl_options[] = {
-#if SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG
+#if defined(SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG)
{
"NETSCAPE_REUSE_CIPHER_CHANGE_BUG", SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG
},
#endif
-#if SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG
+#if defined(SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG)
{
"SSLREF2_REUSE_CERT_TYPE_BUG", SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG
},
#endif
-#if SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER
+#if defined(SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER)
{
"MICROSOFT_BIG_SSLV3_BUFFER", SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER
},
#endif
-#if SSL_OP_SSLEAY_080_CLIENT_DH_BUG
+#if defined(SSL_OP_SSLEAY_080_CLIENT_DH_BUG)
{
"SSLEAY_080_CLIENT_DH_BUG", SSL_OP_SSLEAY_080_CLIENT_DH_BUG
},
#endif
-#if SSL_OP_TLS_D5_BUG
+#if defined(SSL_OP_TLS_D5_BUG)
{
"TLS_D5_BUG", SSL_OP_TLS_D5_BUG
},
#endif
-#if SSL_OP_TLS_BLOCK_PADDING_BUG
+#if defined(SSL_OP_TLS_BLOCK_PADDING_BUG)
{
"TLS_BLOCK_PADDING_BUG", SSL_OP_TLS_BLOCK_PADDING_BUG
},
#endif
-#if SSL_OP_TLS_ROLLBACK_BUG
+#if defined(SSL_OP_TLS_ROLLBACK_BUG)
{
"TLS_ROLLBACK_BUG", SSL_OP_TLS_ROLLBACK_BUG
},
#endif
-#if SSL_OP_ALL
+#if defined(SSL_OP_ALL)
{
"ALL", (long)SSL_OP_ALL
},
#endif
-#if SSL_OP_SINGLE_DH_USE
+#if defined(SSL_OP_SINGLE_DH_USE)
{
"SINGLE_DH_USE", SSL_OP_SINGLE_DH_USE
},
#endif
-#if SSL_OP_EPHEMERAL_RSA
+#if defined(SSL_OP_EPHEMERAL_RSA)
{
"EPHEMERAL_RSA", SSL_OP_EPHEMERAL_RSA
},
#endif
-#if SSL_OP_PKCS1_CHECK_1
+#if defined(SSL_OP_PKCS1_CHECK_1)
{
"PKCS1_CHECK_1", SSL_OP_PKCS1_CHECK_1
},
#endif
-#if SSL_OP_PKCS1_CHECK_2
+#if defined(SSL_OP_PKCS1_CHECK_2)
{
"PKCS1_CHECK_2", SSL_OP_PKCS1_CHECK_2
},
#endif
-#if SSL_OP_NETSCAPE_CA_DN_BUG
+#if defined(SSL_OP_NETSCAPE_CA_DN_BUG)
{
"NETSCAPE_CA_DN_BUG", SSL_OP_NETSCAPE_CA_DN_BUG
},
#endif
-#if SSL_OP_NON_EXPORT_FIRST
+#if defined(SSL_OP_NON_EXPORT_FIRST)
{
"NON_EXPORT_FIRST", SSL_OP_NON_EXPORT_FIRST
},
#endif
-#if SSL_OP_CIPHER_SERVER_PREFERENCE
+#if defined(SSL_OP_CIPHER_SERVER_PREFERENCE)
{
"CIPHER_SERVER_PREFERENCE", SSL_OP_CIPHER_SERVER_PREFERENCE
},
#endif
-#if SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG
+#if defined(SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG)
{
"NETSCAPE_DEMO_CIPHER_CHANGE_BUG", SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG
},
#endif
-#if SSL_OP_NO_SSLv3
+#if defined(SSL_OP_NO_SSLv3)
{
"NO_SSLv3", SSL_OP_NO_SSLv3
},
#endif
-#if SSL_OP_NO_TLSv1
+#if defined(SSL_OP_NO_TLSv1)
{
"NO_TLSv1", SSL_OP_NO_TLSv1
},
#else
{ "NO_TLSv1", 0 },
#endif
-#if SSL_OP_NO_TLSv1_1
+#if defined(SSL_OP_NO_TLSv1_1)
{
"NO_TLSv1_1", SSL_OP_NO_TLSv1_1
},
#else
{ "NO_TLSv1_1", 0 },
#endif
-#if SSL_OP_NO_TLSv1_2
+#if defined(SSL_OP_NO_TLSv1_2)
{
"NO_TLSv1_2", SSL_OP_NO_TLSv1_2
},
#else
{ "NO_TLSv1_2", 0 },
#endif
-#if SSL_OP_NO_TLSv1_3
+#if defined(SSL_OP_NO_TLSv1_3)
{
"NO_TLSv1_3", SSL_OP_NO_TLSv1_3
},
#else
{ "NO_TLSv1_3", 0 },
#endif
-#if SSL_OP_NO_COMPRESSION
+#if defined(SSL_OP_NO_COMPRESSION)
{
"No_Compression", SSL_OP_NO_COMPRESSION
},
#endif
-#if SSL_OP_NO_TICKET
+#if defined(SSL_OP_NO_TICKET)
{
"NO_TICKET", SSL_OP_NO_TICKET
},
#endif
-#if SSL_OP_SINGLE_ECDH_USE
+#if defined(SSL_OP_SINGLE_ECDH_USE)
{
"SINGLE_ECDH_USE", SSL_OP_SINGLE_ECDH_USE
},
#if USE_OPENSSL
::Parser::Tokenizer tok(str);
- long op = 0;
+ ParsedOptions op = 0;
while (!tok.atEnd()) {
enum {
static const CharacterSet optChars = CharacterSet("TLS-option", "_") + CharacterSet::ALPHA + CharacterSet::DIGIT;
int64_t hex = 0;
SBuf option;
- long value = 0;
+ ParsedOptions value = 0;
+ bool found = false;
// Bug 4429: identify the full option name before determining text or numeric
if (tok.prefix(option, optChars)) {
for (struct ssl_option *opttmp = ssl_options; opttmp->name; ++opttmp) {
if (option.cmp(opttmp->name) == 0) {
value = opttmp->value;
+ found = true;
break;
}
}
// Special case.. hex specification
::Parser::Tokenizer tmp(option);
- if (!value && tmp.int64(hex, 16, false) && tmp.atEnd()) {
+ if (!found && tmp.int64(hex, 16, false) && tmp.atEnd()) {
value = hex;
+ found = true;
}
}
break;
}
} else {
- debugs(83, DBG_PARSE_NOTE(1), "ERROR: Unknown TLS option " << option);
+ debugs(83, DBG_PARSE_NOTE(DBG_IMPORTANT), "ERROR: " << (found?"Unsupported":"Unknown") << " TLS option " << option);
}
static const CharacterSet delims("TLS-option-delim",":,");
}
-#if SSL_OP_NO_SSLv2
+#if defined(SSL_OP_NO_SSLv2)
// compliance with RFC 6176: Prohibiting Secure Sockets Layer (SSL) Version 2.0
- op = op | SSL_OP_NO_SSLv2;
+ if (SSL_OP_NO_SSLv2)
+ op |= SSL_OP_NO_SSLv2;
#endif
parsedOptions = op;
#include "anyp/PortCfg.h"
#include "base/Packable.h"
#include "cache_cf.h"
+#include "error/SysErrorDetail.h"
#include "fatal.h"
#include "globals.h"
#include "security/ServerOptions.h"
#include "compat/openssl.h"
#include "ssl/support.h"
+#if HAVE_OPENSSL_DECODER_H
+#include <openssl/decoder.h>
+#endif
#if HAVE_OPENSSL_ERR_H
#include <openssl/err.h>
#endif
if (dhParamsFile.isEmpty())
return;
+ // TODO: After loading and validating parameters, also validate that "the
+ // public and private components have the correct mathematical
+ // relationship". See EVP_PKEY_check().
+
#if USE_OPENSSL
+#if OPENSSL_VERSION_MAJOR < 3
DH *dhp = nullptr;
if (FILE *in = fopen(dhParamsFile.c_str(), "r")) {
dhp = PEM_read_DHparams(in, nullptr, nullptr, nullptr);
fclose(in);
+ } else {
+ const auto xerrno = errno;
+ debugs(83, DBG_IMPORTANT, "WARNING: Failed to open '" << dhParamsFile << "'" << ReportSysError(xerrno));
+ return;
}
if (!dhp) {
}
parsedDhParams.resetWithoutLocking(dhp);
+
+#else // OpenSSL 3.0+
+ const auto type = eecdhCurve.isEmpty() ? "DH" : "EC";
+
+ Ssl::ForgetErrors();
+ EVP_PKEY *rawPkey = nullptr;
+ using DecoderContext = std::unique_ptr<OSSL_DECODER_CTX, HardFun<void, OSSL_DECODER_CTX*, &OSSL_DECODER_CTX_free> >;
+ if (const DecoderContext dctx{OSSL_DECODER_CTX_new_for_pkey(&rawPkey, "PEM", nullptr, type, 0, nullptr, nullptr)}) {
+
+ // OpenSSL documentation is vague on this, but OpenSSL code and our
+ // tests suggest that rawPkey remains nil here while rawCtx keeps
+ // rawPkey _address_ for use by the decoder (see OSSL_DECODER_from_fp()
+ // below). Thus, we must not move *rawPkey into a smart pointer until
+ // decoding is over. For cleanup code simplicity, we assert nil rawPkey.
+ assert(!rawPkey);
+
+ if (OSSL_DECODER_CTX_get_num_decoders(dctx.get()) == 0) {
+ debugs(83, DBG_IMPORTANT, "WARNING: No suitable decoders found for " << type << " parameters" << Ssl::ReportAndForgetErrors);
+ return;
+ }
+
+ if (const auto in = fopen(dhParamsFile.c_str(), "r")) {
+ if (OSSL_DECODER_from_fp(dctx.get(), in)) {
+ assert(rawPkey);
+ const Security::DhePointer pkey(rawPkey);
+ // TODO: verify that the loaded parameters match the curve named in eecdhCurve
+
+ if (const Ssl::EVP_PKEY_CTX_Pointer pkeyCtx{EVP_PKEY_CTX_new_from_pkey(nullptr, pkey.get(), nullptr)}) {
+ switch (EVP_PKEY_param_check(pkeyCtx.get())) {
+ case 1: // success
+ parsedDhParams = pkey;
+ break;
+ case -2:
+ debugs(83, DBG_PARSE_NOTE(2), "WARNING: OpenSSL does not support " << type << " parameters check: " << dhParamsFile << Ssl::ReportAndForgetErrors);
+ break;
+ default:
+ debugs(83, DBG_IMPORTANT, "ERROR: Failed to verify " << type << " parameters in " << dhParamsFile << Ssl::ReportAndForgetErrors);
+ break;
+ }
+ } else {
+ // TODO: Reduce error reporting code duplication.
+ debugs(83, DBG_IMPORTANT, "ERROR: Cannot check " << type << " parameters in " << dhParamsFile << Ssl::ReportAndForgetErrors);
+ }
+ } else {
+ debugs(83, DBG_IMPORTANT, "WARNING: Failed to decode " << type << " parameters '" << dhParamsFile << "'" << Ssl::ReportAndForgetErrors);
+ EVP_PKEY_free(rawPkey); // probably still nil, but just in case
+ }
+ fclose(in);
+ } else {
+ const auto xerrno = errno;
+ debugs(83, DBG_IMPORTANT, "WARNING: Failed to open '" << dhParamsFile << "'" << ReportSysError(xerrno));
+ }
+
+ } else {
+ debugs(83, DBG_IMPORTANT, "WARNING: Unable to create decode context for " << type << " parameters" << Ssl::ReportAndForgetErrors);
+ return;
+ }
#endif
+#endif // USE_OPENSSL
}
bool
debugs(83, 9, "Setting Ephemeral ECDH curve to " << eecdhCurve << ".");
#if USE_OPENSSL && OPENSSL_VERSION_NUMBER >= 0x0090800fL && !defined(OPENSSL_NO_ECDH)
+
+ Ssl::ForgetErrors();
+
int nid = OBJ_sn2nid(eecdhCurve.c_str());
if (!nid) {
debugs(83, DBG_CRITICAL, "ERROR: Unknown EECDH curve '" << eecdhCurve << "'");
return;
}
+#if OPENSSL_VERSION_MAJOR < 3
auto ecdh = EC_KEY_new_by_curve_name(nid);
if (!ecdh) {
const auto x = ERR_get_error();
}
EC_KEY_free(ecdh);
+#else
+ // TODO: Support multiple group names via SSL_CTX_set1_groups_list().
+ if (!SSL_CTX_set1_groups(ctx.get(), &nid, 1)) {
+ debugs(83, DBG_CRITICAL, "ERROR: Unable to set Ephemeral ECDH: " << Ssl::ReportAndForgetErrors);
+ return;
+ }
+#endif
#else
debugs(83, DBG_CRITICAL, "ERROR: EECDH is not available in this build." <<
" Please link against OpenSSL>=0.9.8 and ensure OPENSSL_NO_ECDH is not set.");
typedef std::list<Security::CrlPointer> CertRevokeList;
#if USE_OPENSSL
+CtoCpp1(EVP_PKEY_free, EVP_PKEY *)
+using PrivateKeyPointer = Security::LockingPointer<EVP_PKEY, EVP_PKEY_free_cpp, HardFun<int, EVP_PKEY *, EVP_PKEY_up_ref>>;
+#elif USE_GNUTLS
+using PrivateKeyPointer = std::shared_ptr<struct gnutls_x509_privkey_int>;
+#else
+using PrivateKeyPointer = std::shared_ptr<void>;
+#endif
+
+#if USE_OPENSSL
+#if OPENSSL_VERSION_MAJOR < 3
CtoCpp1(DH_free, DH *);
typedef Security::LockingPointer<DH, DH_free_cpp, HardFun<int, DH *, DH_up_ref> > DhePointer;
#else
-typedef void *DhePointer;
+using DhePointer = PrivateKeyPointer;
+#endif
+#elif USE_GNUTLS
+using DhePointer = void *;
+#else
+using DhePointer = void *;
#endif
class EncryptorAnswer;
class KeyLog;
#if USE_OPENSSL
-typedef long ParsedOptions;
+using ParsedOptions = uint64_t;
#elif USE_GNUTLS
typedef std::shared_ptr<struct gnutls_priority_st> ParsedOptions;
#else
class BlindPeerConnector;
class PeerOptions;
-#if USE_OPENSSL
-CtoCpp1(EVP_PKEY_free, EVP_PKEY *)
-typedef Security::LockingPointer<EVP_PKEY, EVP_PKEY_free_cpp, HardFun<int, EVP_PKEY *, EVP_PKEY_up_ref> > PrivateKeyPointer;
-#elif USE_GNUTLS
-typedef std::shared_ptr<struct gnutls_x509_privkey_int> PrivateKeyPointer;
-#else
-typedef std::shared_ptr<void> PrivateKeyPointer;
-#endif
-
class ServerOptions;
class ErrorDetail;
where);
}
-EVP_PKEY * Ssl::createSslPrivateKey()
+static Security::PrivateKeyPointer
+CreateRsaPrivateKey()
{
- Security::PrivateKeyPointer pkey(EVP_PKEY_new());
-
- if (!pkey)
- return nullptr;
-
- BIGNUM_Pointer bn(BN_new());
- if (!bn)
- return nullptr;
-
- if (!BN_set_word(bn.get(), RSA_F4))
+ Ssl::EVP_PKEY_CTX_Pointer rsa(EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, nullptr));
+ if (!rsa)
return nullptr;
- Ssl::RSA_Pointer rsa(RSA_new());
- if (!rsa)
+ if (EVP_PKEY_keygen_init(rsa.get()) <= 0)
return nullptr;
int num = 2048; // Maybe use 4096 RSA keys, or better make it configurable?
- if (!RSA_generate_key_ex(rsa.get(), num, bn.get(), nullptr))
+ if (EVP_PKEY_CTX_set_rsa_keygen_bits(rsa.get(), num) <= 0)
return nullptr;
- if (!EVP_PKEY_assign_RSA(pkey.get(), (rsa.get())))
+ /* Generate key */
+ EVP_PKEY *pkey = nullptr;
+ if (EVP_PKEY_keygen(rsa.get(), &pkey) <= 0)
return nullptr;
- rsa.release();
- return pkey.release();
+ return Security::PrivateKeyPointer(pkey);
}
/**
if (!bn)
return false;
- if (!BN_pseudo_rand(bn.get(), 64, 0, 0))
+ if (!BN_rand(bn.get(), 64, 0, 0))
return false;
}
// XXX: Add PublicKeyPointer. In OpenSSL, public and private keys are
// internally represented by EVP_PKEY pair, but GnuTLS uses distinct types.
const Security::PrivateKeyPointer certKey(X509_get_pubkey(mimicCert.get()));
+#if OPENSSL_VERSION_MAJOR < 3
const auto rsaPkey = EVP_PKEY_get0_RSA(certKey.get()) != nullptr;
+#else
+ const auto rsaPkey = EVP_PKEY_is_a(certKey.get(), "RSA") == 1;
+#endif
int added = 0;
int nid;
static bool generateFakeSslCertificate(Security::CertPointer & certToStore, Security::PrivateKeyPointer & pkeyToStore, Ssl::CertificateProperties const &properties, Ssl::BIGNUM_Pointer const &serial)
{
- Security::PrivateKeyPointer pkey;
// Use signing certificates private key as generated certificate private key
- if (properties.signWithPkey.get())
- pkey.resetAndLock(properties.signWithPkey.get());
- else // if not exist generate one
- pkey.resetWithoutLocking(Ssl::createSslPrivateKey());
-
+ const auto pkey = properties.signWithPkey ? properties.signWithPkey : CreateRsaPrivateKey();
if (!pkey)
return false;
typedef std::unique_ptr<X509_NAME, HardFun<void, X509_NAME*, &X509_NAME_free>> X509_NAME_Pointer;
-typedef std::unique_ptr<RSA, HardFun<void, RSA*, &RSA_free>> RSA_Pointer;
+using EVP_PKEY_CTX_Pointer = std::unique_ptr<EVP_PKEY_CTX, HardFun<void, EVP_PKEY_CTX*, &EVP_PKEY_CTX_free>>;
typedef std::unique_ptr<X509_REQ, HardFun<void, X509_REQ*, &X509_REQ_free>> X509_REQ_Pointer;
/// Also clears all reported errors.
std::ostream &ReportAndForgetErrors(std::ostream &);
-/**
- \ingroup SslCrtdSslAPI
- * Create 1024 bits rsa key.
- */
-EVP_PKEY * createSslPrivateKey();
-
/**
\ingroup SslCrtdSslAPI
* Write private key and SSL certificate to memory.
}
// "dup" function for SSL_get_ex_new_index("cert_err_check")
-#if SQUID_USE_CONST_CRYPTO_EX_DATA_DUP
+#if OPENSSL_VERSION_MAJOR >= 3
+static int
+ssl_dupAclChecklist(CRYPTO_EX_DATA *, const CRYPTO_EX_DATA *, void **,
+ int, long, void *)
+#elif SQUID_USE_CONST_CRYPTO_EX_DATA_DUP
static int
ssl_dupAclChecklist(CRYPTO_EX_DATA *, const CRYPTO_EX_DATA *, void *,
int, long, void *)
SQUID_OPENSSL_init_ssl();
-#if !defined(OPENSSL_NO_ENGINE)
if (::Config.SSL.ssl_engine) {
+#if OPENSSL_VERSION_MAJOR < 3
+ debugs(83, DBG_PARSE_NOTE(DBG_IMPORTANT), "WARNING: Support for ssl_engine is deprecated " <<
+ "in Squids built with OpenSSL v1 (like this Squid). " <<
+ "It is removed in Squids built with OpenSSL v3+.");
+#if !defined(OPENSSL_NO_ENGINE)
ENGINE_load_builtin_engines();
ENGINE *e;
if (!(e = ENGINE_by_id(::Config.SSL.ssl_engine)))
const auto ssl_error = ERR_get_error();
fatalf("Failed to initialise SSL engine: %s\n", Security::ErrorString(ssl_error));
}
- }
-#else
- if (::Config.SSL.ssl_engine)
- fatalf("Your OpenSSL has no SSL engine support\n");
+#else /* OPENSSL_NO_ENGINE */
+ throw TextException("Cannot use ssl_engine in Squid built with OpenSSL configured to disable SSL engine support", Here());
+#endif
+
+#else /* OPENSSL_VERSION_MAJOR */
+ throw TextException("Cannot use ssl_engine in Squid built with OpenSSL v3+", Here());
#endif
+ }
const char *defName = ::Config.SSL.certSignHash ? ::Config.SSL.certSignHash : SQUID_SSL_SIGN_HASH_IF_NONE;
Ssl::DefaultSignHash = EVP_get_digestbyname(defName);