24#if HAVE_OPENSSL_DECODER_H
25#include <openssl/decoder.h>
28#include <openssl/err.h>
64 encryptTransport =
true;
69 if (strncmp(token,
"clientca=", 9) == 0) {
70 clientCaFile =
SBuf(token + 9);
71 }
else if (strncmp(token,
"dh=", 3) == 0) {
80 auto pos = dh.find(
':');
82 eecdhCurve = dh.substr(0,pos);
83 dhParamsFile = dh.substr(pos+1);
92 }
else if (strncmp(token,
"dhparams=", 9) == 0) {
93 if (!eecdhCurve.isEmpty()) {
100 dh.append(token + 9);
105 }
else if (strncmp(token,
"dynamic_cert_mem_cache_size=", 28) == 0) {
109 if (dynamicCertMemCacheSize == std::numeric_limits<size_t>::max()) {
110 debugs(3,
DBG_CRITICAL,
"ERROR: Cannot allocate memory for '" << token <<
"'. Using default of 4MB instead.");
111 dynamicCertMemCacheSize = 4*1024*1024;
114 }
else if (strcmp(token,
"generate-host-certificates") == 0) {
115 generateHostCertificates =
true;
116 }
else if (strcmp(token,
"generate-host-certificates=on") == 0) {
117 generateHostCertificates =
true;
118 }
else if (strcmp(token,
"generate-host-certificates=off") == 0) {
119 generateHostCertificates =
false;
121 }
else if (strncmp(token,
"context=", 8) == 0) {
123 staticContextSessionId =
SBuf(token+8);
125 if (staticContextSessionId.length() > SSL_MAX_SSL_SESSION_ID_LENGTH) {
126 debugs(83,
DBG_CRITICAL,
"FATAL: Option 'context=' value is too long. Maximum " << SSL_MAX_SSL_SESSION_ID_LENGTH <<
" characters.");
145 if (!encryptTransport)
150 os <<
' ' << pfx <<
"dh=" << dh;
152 if (!generateHostCertificates)
153 os <<
' ' << pfx <<
"generate-host-certificates=off";
155 if (dynamicCertMemCacheSize != 4*1024*1024)
156 os <<
' ' <<
"dynamic_cert_mem_cache_size=" << dynamicCertMemCacheSize <<
"bytes";
158 if (!staticContextSessionId.isEmpty())
159 os <<
' ' << pfx <<
"context=" << staticContextSessionId;
171 const auto x = ERR_get_error();
174 ctx = convertContextFromRawPtr(t);
178 gnutls_certificate_credentials_t t;
179 if (
const auto x = gnutls_certificate_allocate_credentials(&t)) {
182 ctx = convertContextFromRawPtr(t);
185 debugs(83,
DBG_CRITICAL,
"ERROR: Failed to allocate TLS server context: No TLS library");
196 for (
auto &keyData : certs) {
197 keyData.loadFromFiles(
port, portType);
200 if (generateHostCertificates) {
201 createSigningContexts(
port);
204 if (!certs.empty() && !createStaticServerContext(
port)) {
206 fatalf(
"%s_port %s initialization error", portType,
port.s.toUrl(buf,
sizeof(buf)));
216 updateTlsVersionLimits();
222 if (certs.size() > 1) {
225 debugs(83,
DBG_CRITICAL,
"ERROR: OpenSSL does not support multiple server certificates. Ignoring additional cert= parameters.");
228 const auto &
keys = certs.front();
230 if (!SSL_CTX_use_certificate(t.get(),
keys.cert.get())) {
231 const auto x = ERR_get_error();
236 if (!SSL_CTX_use_PrivateKey(t.get(),
keys.pkey.get())) {
237 const auto x = ERR_get_error();
242 for (
auto cert :
keys.chain) {
243 if (SSL_CTX_add_extra_chain_cert(t.get(), cert.get())) {
245 X509_up_ref(cert.get());
247 const auto error = ERR_get_error();
253 for (
auto &
keys : certs) {
254 gnutls_x509_crt_t crt =
keys.cert.get();
255 gnutls_x509_privkey_t xkey =
keys.pkey.get();
256 const auto x = gnutls_certificate_set_x509_key(t.get(), &crt, 1, xkey);
257 if (x != GNUTLS_E_SUCCESS) {
259 if (
keys.certFile !=
keys.privateKeyFile) {
270 if (!loadClientCaFile())
274 if (!updateContextConfig(t)) {
280 staticContext = std::move(t);
281 return bool(staticContext);
291 signingCa = certs.front();
294 if (!signingCa.cert) {
297 fatalf(
"No valid signing certificate configured for %s_port %s", portType,
port.s.toUrl(buf,
sizeof(buf)));
307 signingCa.cert.reset();
308 signingCa.pkey.reset();
309 debugs(83,
DBG_CRITICAL,
"WARNING: Dynamic TLS certificate generation requires --with-openssl.");
312 debugs(83,
DBG_CRITICAL,
"ERROR: Dynamic TLS certificate generation requires --with-openssl.");
316 if (!untrustedSigningCa.cert) {
318 fatalf(
"Unable to generate signing certificate for untrusted sites for %s_port %s", portType,
port.s.toUrl(buf,
sizeof(buf)));
330 if (!clientCaFile.isEmpty())
331 caFiles.emplace_back(clientCaFile);
340 if (clientCaFile.isEmpty())
344 auto *stk = SSL_load_client_CA_file(clientCaFile.c_str());
347 if (!clientCaStack) {
348 debugs(83,
DBG_CRITICAL,
"FATAL: Unable to read client CAs from file: " << clientCaFile);
351 return bool(clientCaStack);
363 if (dhParamsFile.isEmpty())
371#if OPENSSL_VERSION_MAJOR < 3
373 if (FILE *in = fopen(dhParamsFile.c_str(),
"r")) {
374 dhp = PEM_read_DHparams(in,
nullptr,
nullptr,
nullptr);
377 const auto xerrno = errno;
383 debugs(83,
DBG_IMPORTANT,
"WARNING: Failed to read DH parameters '" << dhParamsFile <<
"'");
388 if (DH_check(dhp, &codes) == 0) {
390 debugs(83,
DBG_IMPORTANT,
"WARNING: Failed to verify DH parameters '" << dhParamsFile <<
"' (" <<
asHex(codes) <<
")");
396 parsedDhParams.resetWithoutLocking(dhp);
399 const auto type =
"DH";
402 EVP_PKEY *rawPkey =
nullptr;
403 using DecoderContext = std::unique_ptr<OSSL_DECODER_CTX, HardFun<void, OSSL_DECODER_CTX*, &OSSL_DECODER_CTX_free> >;
404 if (
const DecoderContext dctx{OSSL_DECODER_CTX_new_for_pkey(&rawPkey,
"PEM",
nullptr, type, 0,
nullptr,
nullptr)}) {
413 if (OSSL_DECODER_CTX_get_num_decoders(dctx.get()) == 0) {
418 if (
const auto in = fopen(dhParamsFile.c_str(),
"r")) {
419 if (OSSL_DECODER_from_fp(dctx.get(), in)) {
423 switch (EVP_PKEY_param_check(pkeyCtx.get())) {
425 parsedDhParams = pkey;
440 EVP_PKEY_free(rawPkey);
444 const auto xerrno = errno;
459 updateContextOptions(ctx);
460 updateContextSessionId(ctx);
464 SSL_CTX_set_session_cache_mode(ctx.get(), SSL_SESS_CACHE_OFF);
468 debugs(83, 5,
"Enabling quiet SSL shutdowns (RFC violation).");
469 SSL_CTX_set_quiet_shutdown(ctx.get(), 1);
472 if (!sslCipher.isEmpty()) {
473 debugs(83, 5,
"Using cipher suite " << sslCipher <<
".");
474 if (!SSL_CTX_set_cipher_list(ctx.get(), sslCipher.c_str())) {
475 auto ssl_error = ERR_get_error();
484 updateContextEecdh(ctx);
485 updateContextCa(ctx);
486 updateContextClientCa(ctx);
489 SSL_CTX_set_mode(ctx.get(), SSL_MODE_NO_AUTO_CHAIN);
504 if (
STACK_OF(X509_NAME) *clientca = SSL_dup_CA_list(clientCaStack.get())) {
505 SSL_CTX_set_client_CA_list(ctx.get(), clientca);
507 auto ssl_error = ERR_get_error();
514 updateContextCrl(ctx);
515 updateContextTrust(ctx);
529 if (!eecdhCurve.isEmpty()) {
530 debugs(83, 9,
"Setting Ephemeral ECDH curve to " << eecdhCurve <<
".");
532#if USE_OPENSSL && OPENSSL_VERSION_NUMBER >= 0x0090800fL && !defined(OPENSSL_NO_ECDH)
536 int nid = OBJ_sn2nid(eecdhCurve.c_str());
542#if OPENSSL_VERSION_MAJOR < 3
543 auto ecdh = EC_KEY_new_by_curve_name(nid);
545 const auto x = ERR_get_error();
550 if (!SSL_CTX_set_tmp_ecdh(ctx.get(), ecdh)) {
551 const auto x = ERR_get_error();
558 if (!SSL_CTX_set1_groups(ctx.get(), &nid, 1)) {
565 " Please link against OpenSSL>=0.9.8 and ensure OPENSSL_NO_ECDH is not set.");
572 if (parsedDhParams) {
573#if OPENSSL_VERSION_MAJOR < 3
574 if (SSL_CTX_set_tmp_dh(ctx.get(), parsedDhParams.get()) != 1) {
578 const auto tmp = EVP_PKEY_dup(parsedDhParams.get());
583 if (SSL_CTX_set0_tmp_dh_pkey(ctx.get(), tmp) != 1) {
596 if (!staticContextSessionId.isEmpty())
597 SSL_CTX_set_session_id_context(ctx.get(),
reinterpret_cast<const unsigned char*
>(staticContextSessionId.rawContent()), staticContextSessionId.length());
AsHex< Integer > asHex(const Integer n)
a helper to ease AsHex object creation
void error(char *format,...)
void parseBytesOptionValue(size_t *bptr, const char *units, char const *value)
Parse bytes number from a string.
a stream manipulator for printing a system call error (if any)
static const size_type npos
SBuf & appendf(const char *fmt,...) PRINTF_FORMAT_ARG2
SBuf & append(const SBuf &S)
T * get() const
Returns raw and possibly nullptr pointer.
virtual void parse(const char *)
parse a TLS squid.conf option
PeerOptions & operator=(const PeerOptions &)=default
virtual void dumpCfg(std::ostream &, const char *pfx) const
output squid.conf syntax with 'pfx' prefix on parameters for the stored settings
TLS squid.conf settings for a listening port.
size_t dynamicCertMemCacheSize
max size of generated certificates memory cache (4 MB default)
void createSigningContexts(const AnyP::PortCfg &)
Security::ContextPointer createBlankContext() const override
generate an unset security context object
void updateContextEecdh(Security::ContextPointer &)
update the context with DH, EDH, EECDH settings
SBuf dhParamsFile
Diffi-Helman ciphers parameter file.
X509_NAME_STACK_Pointer clientCaStack
CA certificate(s) to use when verifying client certificates.
bool createStaticServerContext(AnyP::PortCfg &)
SBuf eecdhCurve
Elliptic curve for ephemeral EC-based DH key exchanges.
Security::KeyData untrustedSigningCa
x509 certificate and key for signing untrusted generated certificates
void parse(const char *) override
parse a TLS squid.conf option
bool generateHostCertificates
dynamically make host cert
void dumpCfg(std::ostream &, const char *pfx) const override
output squid.conf syntax with 'pfx' prefix on parameters for the stored settings
SBuf staticContextSessionId
"session id context" for staticContext
void initServerContexts(AnyP::PortCfg &)
SBuf dh
Diffi-Helman cipher config.
void syncCaFiles()
sync the various sources of CA files to be loaded
Security::DhePointer parsedDhParams
DH parameters for temporary/ephemeral DH key exchanges.
bool updateContextConfig(Security::ContextPointer &)
update the given TLS security context using squid.conf settings
ServerOptions & operator=(const ServerOptions &)
std::unique_ptr< STACK_OF(X509_NAME), Security::ServerOptions::sk_X509_NAME_free_wrapper > X509_NAME_STACK_Pointer
void updateContextSessionId(Security::ContextPointer &)
update the context with a configured session ID (if any)
SBuf clientCaFile
name of file to load client CAs from
Security::KeyData signingCa
x509 certificate and key for signing generated certificates
void updateContextClientCa(Security::ContextPointer &)
update the context with CA details used to verify client certificates
struct SquidConfig::@97 SSL
#define DBG_PARSE_NOTE(x)
#define debugs(SECTION, LEVEL, CONTENT)
void fatalf(const char *fmt,...)
int ssl_ctx_ex_index_dont_verify_domain
bool generateUntrustedCert(Security::CertPointer &untrustedCert, Security::PrivateKeyPointer &untrustedPkey, Security::CertPointer const &cert, Security::PrivateKeyPointer const &pkey)
const char * ProtocolType_str[]
void SetSessionCacheCallbacks(Security::ContextPointer &)
Setup the given TLS context with callbacks used to manage the session cache.
std::shared_ptr< SSL_CTX > ContextPointer
const char * ErrorString(const LibErrorCode code)
converts numeric LibErrorCode into a human-friendlier string
std::unique_ptr< EVP_PKEY_CTX, HardFun< void, EVP_PKEY_CTX *, &EVP_PKEY_CTX_free > > EVP_PKEY_CTX_Pointer
void DisablePeerVerification(Security::ContextPointer &)
std::ostream & ReportAndForgetErrors(std::ostream &)
void MaybeSetupRsaCallback(Security::ContextPointer &)
if required, setup callback for generating ephemeral RSA keys
void ForgetErrors()
Clear any errors accumulated by OpenSSL in its global storage.
void ConfigurePeerVerification(Security::ContextPointer &, const Security::ParsedPortFlags)
set the certificate verify callback for a context
#define TLS_server_method
STACK_OF(X509) *X509_STORE_CTX_get0_untrusted(X509_STORE_CTX *ctx)
#define SSL_FLAG_NO_SESSION_REUSE
#define SSL_FLAG_DONT_VERIFY_DOMAIN