ServerOptions.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2019 The Squid Software Foundation and contributors
3  *
4  * Squid software is distributed under GPLv2+ license and includes
5  * contributions from numerous individuals and organizations.
6  * Please see the COPYING and CONTRIBUTORS files for details.
7  */
8 
9 #include "squid.h"
10 #include "anyp/PortCfg.h"
11 #include "base/Packable.h"
12 #include "cache_cf.h"
13 #include "fatal.h"
14 #include "globals.h"
15 #include "security/ServerOptions.h"
16 #include "security/Session.h"
17 #include "SquidConfig.h"
18 #if USE_OPENSSL
19 #include "compat/openssl.h"
20 #include "ssl/support.h"
21 
22 #if HAVE_OPENSSL_ERR_H
23 #include <openssl/err.h>
24 #endif
25 #endif
26 
29  if (this != &old) {
32  dh = old.dh;
34  eecdhCurve = old.eecdhCurve;
36 #if USE_OPENSSL
37  if (auto *stk = SSL_dup_CA_list(old.clientCaStack.get()))
39  else
40 #endif
41  clientCaStack = nullptr;
42 
45  signingCa = old.signingCa;
48  }
49  return *this;
50 }
51 
52 void
54 {
55  if (!*token) {
56  // config says just "ssl" or "tls" (or "tls-")
57  encryptTransport = true;
58  return;
59  }
60 
61  // parse the server-only options
62  if (strncmp(token, "clientca=", 9) == 0) {
63  clientCaFile = SBuf(token + 9);
64  } else if (strncmp(token, "dh=", 3) == 0) {
65  // clear any previous Diffi-Helman configuration
66  dh.clear();
68  eecdhCurve.clear();
69 
70  dh.append(token + 3);
71 
72  if (!dh.isEmpty()) {
73  auto pos = dh.find(':');
74  if (pos != SBuf::npos) { // tls-dh=eecdhCurve:dhParamsFile
75  eecdhCurve = dh.substr(0,pos);
76  dhParamsFile = dh.substr(pos+1);
77  } else { // tls-dh=dhParamsFile
78  dhParamsFile = dh;
79  // empty eecdhCurve means "do not use EECDH"
80  }
81  }
82 
83  loadDhParams();
84 
85  } else if (strncmp(token, "dhparams=", 9) == 0) {
86  if (!eecdhCurve.isEmpty()) {
87  debugs(83, DBG_PARSE_NOTE(1), "UPGRADE WARNING: EECDH settings in tls-dh= override dhparams=");
88  return;
89  }
90 
91  // backward compatibility for dhparams= configuration
92  dh.clear();
93  dh.append(token + 9);
94  dhParamsFile = dh;
95 
96  loadDhParams();
97 
98  } else if (strncmp(token, "dynamic_cert_mem_cache_size=", 28) == 0) {
99  parseBytesOptionValue(&dynamicCertMemCacheSize, "bytes", token + 28);
100  // XXX: parseBytesOptionValue() self_destruct()s on invalid values,
101  // probably making this comparison and misleading ERROR unnecessary.
103  debugs(3, DBG_CRITICAL, "ERROR: Cannot allocate memory for '" << token << "'. Using default of 4MB instead.");
104  dynamicCertMemCacheSize = 4*1024*1024; // 4 MB
105  }
106 
107  } else if (strcmp(token, "generate-host-certificates") == 0) {
109  } else if (strcmp(token, "generate-host-certificates=on") == 0) {
111  } else if (strcmp(token, "generate-host-certificates=off") == 0) {
112  generateHostCertificates = false;
113 
114  } else if (strncmp(token, "context=", 8) == 0) {
115 #if USE_OPENSSL
116  staticContextSessionId = SBuf(token+8);
117  // to hide its arguably sensitive value, do not print token in these debugs
118  if (staticContextSessionId.length() > SSL_MAX_SSL_SESSION_ID_LENGTH) {
119  debugs(83, DBG_CRITICAL, "FATAL: Option 'context=' value is too long. Maximum " << SSL_MAX_SSL_SESSION_ID_LENGTH << " characters.");
120  self_destruct();
121  }
122 #else
123  debugs(83, DBG_PARSE_NOTE(DBG_IMPORTANT), "WARNING: Option 'context=' requires --with-openssl. Ignoring.");
124 #endif
125 
126  } else {
127  // parse generic TLS options
129  }
130 }
131 
132 void
134 {
135  // dump out the generic TLS options
137 
138  if (!encryptTransport)
139  return; // no other settings are relevant
140 
141  // dump the server-only options
142  if (!dh.isEmpty())
143  p->appendf(" %sdh=" SQUIDSBUFPH, pfx, SQUIDSBUFPRINT(dh));
144 
146  p->appendf(" %sgenerate-host-certificates=off", pfx);
147 
148  if (dynamicCertMemCacheSize != 4*1024*1024) // 4MB default, no 'tls-' prefix
149  p->appendf(" dynamic_cert_mem_cache_size=%" PRIuSIZE "bytes", dynamicCertMemCacheSize);
150 
152  p->appendf(" %scontext=" SQUIDSBUFPH, pfx, SQUIDSBUFPRINT(staticContextSessionId));
153 }
154 
157 {
159 #if USE_OPENSSL
160  Ssl::Initialize();
161 
162  SSL_CTX *t = SSL_CTX_new(TLS_server_method());
163  if (!t) {
164  const auto x = ERR_get_error();
165  debugs(83, DBG_CRITICAL, "ERROR: Failed to allocate TLS server context: " << Security::ErrorString(x));
166  }
167  ctx = convertContextFromRawPtr(t);
168 
169 #elif USE_GNUTLS
170  // Initialize for X.509 certificate exchange
171  gnutls_certificate_credentials_t t;
172  if (const int x = gnutls_certificate_allocate_credentials(&t)) {
173  debugs(83, DBG_CRITICAL, "ERROR: Failed to allocate TLS server context: " << Security::ErrorString(x));
174  }
175  ctx = convertContextFromRawPtr(t);
176 
177 #else
178  debugs(83, DBG_CRITICAL, "ERROR: Failed to allocate TLS server context: No TLS library");
179 
180 #endif
181 
182  return ctx;
183 }
184 
185 void
187 {
188  const char *portType = AnyP::ProtocolType_str[port.transport.protocol];
189  for (auto &keyData : certs) {
190  keyData.loadFromFiles(port, portType);
191  }
192 
194  createSigningContexts(port);
195  }
196 
197  if (!certs.empty() && !createStaticServerContext(port)) {
198  char buf[128];
199  fatalf("%s_port %s initialization error", portType, port.s.toUrl(buf, sizeof(buf)));
200  }
201 
202  // if generate-host-certificates=off and certs is empty, no contexts may be created.
203  // features depending on contexts do their own checks and error messages later.
204 }
205 
206 bool
208 {
210 
212  if (t) {
213 
214 #if USE_OPENSSL
215  if (certs.size() > 1) {
216  // NOTE: calling SSL_CTX_use_certificate() repeatedly _replaces_ the previous cert details.
217  // so we cannot use it and support multiple server certificates with OpenSSL.
218  debugs(83, DBG_CRITICAL, "ERROR: OpenSSL does not support multiple server certificates. Ignoring addional cert= parameters.");
219  }
220 
221  const auto &keys = certs.front();
222 
223  if (!SSL_CTX_use_certificate(t.get(), keys.cert.get())) {
224  const auto x = ERR_get_error();
225  debugs(83, DBG_CRITICAL, "ERROR: Failed to acquire TLS certificate '" << keys.certFile << "': " << Security::ErrorString(x));
226  return false;
227  }
228 
229  if (!SSL_CTX_use_PrivateKey(t.get(), keys.pkey.get())) {
230  const auto x = ERR_get_error();
231  debugs(83, DBG_CRITICAL, "ERROR: Failed to acquire TLS private key '" << keys.privateKeyFile << "': " << Security::ErrorString(x));
232  return false;
233  }
234 
235  for (auto cert : keys.chain) {
236  if (SSL_CTX_add_extra_chain_cert(t.get(), cert.get())) {
237  // increase the certificate lock
238  X509_up_ref(cert.get());
239  } else {
240  const auto error = ERR_get_error();
241  debugs(83, DBG_IMPORTANT, "WARNING: can not add certificate to SSL context chain: " << Security::ErrorString(error));
242  }
243  }
244 
245 #elif USE_GNUTLS
246  for (auto &keys : certs) {
247  gnutls_x509_crt_t crt = keys.cert.get();
248  gnutls_x509_privkey_t xkey = keys.pkey.get();
249  const auto x = gnutls_certificate_set_x509_key(t.get(), &crt, 1, xkey);
250  if (x != GNUTLS_E_SUCCESS) {
251  SBuf whichFile = keys.certFile;
252  if (keys.certFile != keys.privateKeyFile) {
253  whichFile.appendf(" and ");
254  whichFile.append(keys.privateKeyFile);
255  }
256  debugs(83, DBG_CRITICAL, "ERROR: Failed to initialize server context with keys from " << whichFile << ": " << Security::ErrorString(x));
257  return false;
258  }
259  // XXX: add cert chain to the context
260  }
261 #endif
262 
263  if (!loadClientCaFile())
264  return false;
265 
266  // by this point all config related files must be loaded
267  if (!updateContextConfig(t)) {
268  debugs(83, DBG_CRITICAL, "ERROR: Configuring static TLS context");
269  return false;
270  }
271  }
272 
273  staticContext = std::move(t);
274  return bool(staticContext);
275 }
276 
277 void
279 {
280  // For signing we do not have a pre-initialized context object. Instead
281  // contexts are generated as needed. This method initializes the cert
282  // and key pointers used to sign those contexts later.
283 
284  signingCa = certs.front();
285 
286  const char *portType = AnyP::ProtocolType_str[port.transport.protocol];
287  if (!signingCa.cert) {
288  char buf[128];
289  // XXX: we never actually checked that the cert is capable of signing!
290  fatalf("No valid signing certificate configured for %s_port %s", portType, port.s.toUrl(buf, sizeof(buf)));
291  }
292 
293  if (!signingCa.pkey)
294  debugs(3, DBG_IMPORTANT, "No TLS private key configured for " << portType << "_port " << port.s);
295 
296 #if USE_OPENSSL
298 #elif USE_GNUTLS
299  // TODO: implement for GnuTLS. Just a warning for now since generate is implicitly on for all crypto builds.
300  signingCa.cert.reset();
301  signingCa.pkey.reset();
302  debugs(83, DBG_CRITICAL, "WARNING: Dynamic TLS certificate generation requires --with-openssl.");
303  return;
304 #else
305  debugs(83, DBG_CRITICAL, "ERROR: Dynamic TLS certificate generation requires --with-openssl.");
306  return;
307 #endif
308 
309  if (!untrustedSigningCa.cert) {
310  char buf[128];
311  fatalf("Unable to generate signing certificate for untrusted sites for %s_port %s", portType, port.s.toUrl(buf, sizeof(buf)));
312  }
313 }
314 
315 void
317 {
318  // if caFiles is set, just use that
319  if (caFiles.size())
320  return;
321 
322  // otherwise fall back to clientca if it is defined
323  if (!clientCaFile.isEmpty())
324  caFiles.emplace_back(clientCaFile);
325 }
326 
330 bool
332 {
333  if (clientCaFile.isEmpty())
334  return true;
335 
336 #if USE_OPENSSL
337  auto *stk = SSL_load_client_CA_file(clientCaFile.c_str());
339 #endif
340  if (!clientCaStack) {
341  debugs(83, DBG_CRITICAL, "FATAL: Unable to read client CAs from file: " << clientCaFile);
342  }
343 
344  return bool(clientCaStack);
345 }
346 
347 void
349 {
350  if (dhParamsFile.isEmpty())
351  return;
352 
353 #if USE_OPENSSL
354  DH *dhp = nullptr;
355  if (FILE *in = fopen(dhParamsFile.c_str(), "r")) {
356  dhp = PEM_read_DHparams(in, NULL, NULL, NULL);
357  fclose(in);
358  }
359 
360  if (!dhp) {
361  debugs(83, DBG_IMPORTANT, "WARNING: Failed to read DH parameters '" << dhParamsFile << "'");
362  return;
363  }
364 
365  int codes;
366  if (DH_check(dhp, &codes) == 0) {
367  if (codes) {
368  debugs(83, DBG_IMPORTANT, "WARNING: Failed to verify DH parameters '" << dhParamsFile << "' (" << std::hex << codes << ")");
369  DH_free(dhp);
370  dhp = nullptr;
371  }
372  }
373 
375 #endif
376 }
377 
378 bool
380 {
383 
384 #if USE_OPENSSL
386  SSL_CTX_set_session_cache_mode(ctx.get(), SSL_SESS_CACHE_OFF);
387  }
388 
390  debugs(83, 5, "Enabling quiet SSL shutdowns (RFC violation).");
391  SSL_CTX_set_quiet_shutdown(ctx.get(), 1);
392  }
393 
394  if (!sslCipher.isEmpty()) {
395  debugs(83, 5, "Using cipher suite " << sslCipher << ".");
396  if (!SSL_CTX_set_cipher_list(ctx.get(), sslCipher.c_str())) {
397  auto ssl_error = ERR_get_error();
398  debugs(83, DBG_CRITICAL, "ERROR: Failed to set SSL cipher suite '" << sslCipher << "': " << Security::ErrorString(ssl_error));
399  return false;
400  }
401  }
402 
404 #endif
405 
406  updateContextEecdh(ctx);
407  updateContextCa(ctx);
409 
410 #if USE_OPENSSL
412  SSL_CTX_set_ex_data(ctx.get(), ssl_ctx_ex_index_dont_verify_domain, (void *) -1);
413 
415 #endif
416  return true;
417 }
418 
419 void
421 {
422 #if USE_OPENSSL
423  if (clientCaStack) {
424  ERR_clear_error();
425  if (STACK_OF(X509_NAME) *clientca = SSL_dup_CA_list(clientCaStack.get())) {
426  SSL_CTX_set_client_CA_list(ctx.get(), clientca);
427  } else {
428  auto ssl_error = ERR_get_error();
429  debugs(83, DBG_CRITICAL, "ERROR: Failed to dupe the client CA list: " << Security::ErrorString(ssl_error));
430  return;
431  }
432 
434  debugs(83, 9, "Not requesting client certificates until acl processing requires one");
435  SSL_CTX_set_verify(ctx.get(), SSL_VERIFY_NONE, nullptr);
436  } else {
437  debugs(83, 9, "Requiring client certificates.");
439  }
440 
441  updateContextCrl(ctx);
442  updateContextTrust(ctx);
443 
444  } else {
445  debugs(83, 9, "Not requiring any client certificates");
446  SSL_CTX_set_verify(ctx.get(), SSL_VERIFY_NONE, NULL);
447  }
448 #endif
449 }
450 
451 void
453 {
454  // set Elliptic Curve details into the server context
455  if (!eecdhCurve.isEmpty()) {
456  debugs(83, 9, "Setting Ephemeral ECDH curve to " << eecdhCurve << ".");
457 
458 #if USE_OPENSSL && OPENSSL_VERSION_NUMBER >= 0x0090800fL && !defined(OPENSSL_NO_ECDH)
459  int nid = OBJ_sn2nid(eecdhCurve.c_str());
460  if (!nid) {
461  debugs(83, DBG_CRITICAL, "ERROR: Unknown EECDH curve '" << eecdhCurve << "'");
462  return;
463  }
464 
465  auto ecdh = EC_KEY_new_by_curve_name(nid);
466  if (!ecdh) {
467  const auto x = ERR_get_error();
468  debugs(83, DBG_CRITICAL, "ERROR: Unable to configure Ephemeral ECDH: " << Security::ErrorString(x));
469  return;
470  }
471 
472  if (!SSL_CTX_set_tmp_ecdh(ctx.get(), ecdh)) {
473  const auto x = ERR_get_error();
474  debugs(83, DBG_CRITICAL, "ERROR: Unable to set Ephemeral ECDH: " << Security::ErrorString(x));
475  }
476  EC_KEY_free(ecdh);
477 
478 #else
479  debugs(83, DBG_CRITICAL, "ERROR: EECDH is not available in this build." <<
480  " Please link against OpenSSL>=0.9.8 and ensure OPENSSL_NO_ECDH is not set.");
481 #endif
482  }
483 
484  // set DH parameters into the server context
485 #if USE_OPENSSL
486  if (parsedDhParams) {
487  SSL_CTX_set_tmp_dh(ctx.get(), parsedDhParams.get());
488  }
489 #endif
490 }
491 
492 void
494 {
495 #if USE_OPENSSL
497  SSL_CTX_set_session_id_context(ctx.get(), reinterpret_cast<const unsigned char*>(staticContextSessionId.rawContent()), staticContextSessionId.length());
498 #endif
499 }
500 
size_type length() const
Returns the number of bytes stored in SBuf.
Definition: SBuf.h:404
Ip::Address s
Definition: PortCfg.h:31
SBuf dh
Diffi-Helman cipher config.
SBuf & appendf(const char *fmt,...)
Definition: SBuf.cc:239
SBuf staticContextSessionId
"session id context" for staticContext
Definition: ServerOptions.h:72
Security::CertPointer cert
public X.509 certificate from certFile
Definition: KeyData.h:31
Definition: SBuf.h:86
AnyP::ProtocolVersion transport
transport protocol and version received by this port
Definition: PortCfg.h:32
bool updateContextConfig(Security::ContextPointer &)
update the given TLS security context using squid.conf settings
void self_destruct(void)
Definition: cache_cf.cc:256
void resetWithoutLocking(T *t)
Reset raw pointer - unlock any previous one and save new one without locking.
Security::ContextPointer convertContextFromRawPtr(T ctx) const
Definition: PeerOptions.h:108
void error(char *format,...)
void updateContextCrl(Security::ContextPointer &)
setup the CRL details for the given context
Definition: PeerOptions.cc:703
SBuf & append(const SBuf &S)
Definition: SBuf.cc:195
T * get() const
Returns raw and possibly nullptr pointer.
void parseBytesOptionValue(size_t *bptr, const char *units, char const *value)
Parse bytes number from a string.
Definition: cache_cf.cc:1275
bool encryptTransport
whether transport encryption (TLS/SSL) is to be used on connections to the peer
Definition: PeerOptions.h:144
void syncCaFiles()
sync the various sources of CA files to be loaded
std::unique_ptr< STACK_OF(X509_NAME), Security::ServerOptions::sk_X509_NAME_free_wrapper > X509_NAME_STACK_Pointer
Definition: ServerOptions.h:30
int unclean_shutdown
Definition: SquidConfig.h:494
void clear()
Definition: SBuf.cc:178
bool isEmpty() const
Definition: SBuf.h:420
#define DBG_CRITICAL
Definition: Debug.h:45
char * p
Definition: membanger.c:43
#define DBG_PARSE_NOTE(x)
Definition: Debug.h:50
A const & max(A const &lhs, A const &rhs)
Security::KeyData untrustedSigningCa
x509 certificate and key for signing untrusted generated certificates
Definition: ServerOptions.h:88
void updateContextClientCa(Security::ContextPointer &)
update the context with CA details used to verify client certificates
char * toUrl(char *buf, unsigned int len) const
Definition: Address.cc:884
void fatalf(const char *fmt,...)
Definition: fatal.cc:68
#define SSL_FLAG_DONT_VERIFY_DOMAIN
Definition: forward.h:48
void createSigningContexts(const AnyP::PortCfg &)
ServerOptions & operator=(const ServerOptions &)
void updateContextCa(Security::ContextPointer &)
setup the CA details for the given context
Definition: PeerOptions.cc:667
SBuf clientCaFile
name of file to load client CAs from
int ssl_ctx_ex_index_dont_verify_domain
struct SquidConfig::@121 SSL
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Debug.h:124
#define DBG_IMPORTANT
Definition: Debug.h:46
#define SSL_FLAG_DELAYED_AUTH
Definition: forward.h:46
Security::KeyData signingCa
x509 certificate and key for signing generated certificates
Definition: ServerOptions.h:87
void updateContextTrust(Security::ContextPointer &)
decide which CAs to trust
Definition: PeerOptions.cc:728
void initServerContexts(AnyP::PortCfg &)
void reset()
Forget the raw pointer - unlock if any value was set. Become a nil pointer.
X509_NAME_STACK_Pointer clientCaStack
CA certificate(s) to use when verifying client certificates.
std::shared_ptr< SSL_CTX > ContextPointer
Definition: Context.h:29
void MaybeSetupRsaCallback(Security::ContextPointer &)
if required, setup callback for generating ephemeral RSA keys
Definition: support.cc:170
STACK_OF(X509) *X509_STORE_CTX_get0_untrusted(X509_STORE_CTX *ctx)
Definition: openssl.h:195
bool createStaticServerContext(AnyP::PortCfg &)
bool generateHostCertificates
dynamically make host cert
Definition: ServerOptions.h:75
const char * c_str()
Definition: SBuf.cc:526
SBuf dhParamsFile
Diffi-Helman ciphers parameter file.
static int port
Definition: ldap_backend.cc:69
virtual void parse(const char *)
parse a TLS squid.conf option
void const char * buf
Definition: stub_helper.cc:16
static char * keys[]
Definition: WinSvc.cc:84
virtual void parse(const char *)
parse a TLS squid.conf option
Definition: PeerOptions.cc:31
void Initialize()
Definition: support.cc:479
SBuf eecdhCurve
Elliptic curve for ephemeral EC-based DH key exchanges.
void updateContextOptions(Security::ContextPointer &)
Setup the library specific &#39;options=&#39; parameters for the given context.
Definition: PeerOptions.cc:614
virtual void dumpCfg(Packable *, const char *pfx) const
output squid.conf syntax with &#39;pfx&#39; prefix on parameters for the stored settings
Definition: PeerOptions.cc:103
void updateContextEecdh(Security::ContextPointer &)
update the context with DH, EDH, EECDH settings
bool generateUntrustedCert(Security::CertPointer &untrustedCert, Security::PrivateKeyPointer &untrustedPkey, Security::CertPointer const &cert, Security::PrivateKeyPointer const &pkey)
Definition: support.cc:1176
SBuf substr(size_type pos, size_type n=npos) const
Definition: SBuf.cc:586
#define SSL_FLAG_NO_SESSION_REUSE
Definition: forward.h:49
std::list< SBuf > caFiles
paths of files containing trusted Certificate Authority
Definition: PeerOptions.h:103
void appendf(const char *fmt,...) PRINTF_FORMAT_ARG2
Append operation with printf-style arguments.
Definition: Packable.h:61
Security::ContextPointer staticContext
TLS context to use for HTTPS accelerator or static SSL-Bump.
Definition: ServerOptions.h:71
size_type find(char c, size_type startPos=0) const
Definition: SBuf.cc:594
static const size_type npos
Definition: SBuf.h:92
TLS squid.conf settings for a listening port.
Definition: ServerOptions.h:25
const char * ProtocolType_str[]
std::list< Security::KeyData > certs
details from the cert= and file= config parameters
Definition: PeerOptions.h:102
size_t dynamicCertMemCacheSize
max size of generated certificates memory cache (4 MB default)
Definition: ServerOptions.h:91
#define SQUIDSBUFPH
Definition: SBuf.h:31
void updateTlsVersionLimits()
sync the context options with tls-min-version=N configuration
Definition: PeerOptions.cc:151
ProtocolType protocol
which protocol this version is for
void SetupVerifyCallback(Security::ContextPointer &)
set the certificate verify callback for a context
Definition: support.cc:393
PeerOptions & operator=(const PeerOptions &)=default
#define SQUIDSBUFPRINT(s)
Definition: SBuf.h:32
const char * ErrorString(const ErrorCode code)
Definition: forward.h:96
void updateContextSessionId(Security::ContextPointer &)
update the context with a configured session ID (if any)
virtual Security::ContextPointer createBlankContext() const
generate an unset security context object
virtual void dumpCfg(Packable *, const char *pfx) const
output squid.conf syntax with &#39;pfx&#39; prefix on parameters for the stored settings
class SquidConfig Config
Definition: SquidConfig.cc:12
#define NULL
Definition: types.h:166
Security::PrivateKeyPointer pkey
private key from privateKeyFile
Definition: KeyData.h:33
long parsedFlags
parsed value of sslFlags
Definition: PeerOptions.h:100
const char * rawContent() const
Definition: SBuf.cc:519
void SetSessionCacheCallbacks(Security::ContextPointer &)
Setup the given TLS context with callbacks used to manage the session cache.
Definition: Session.cc:376
#define TLS_server_method
Definition: openssl.h:158
Security::DhePointer parsedDhParams
DH parameters for temporary/ephemeral DH key exchanges.

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors