PeerOptions.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2025 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 "base/Packable.h"
11 #include "debug/Stream.h"
12 #include "fatal.h"
13 #include "globals.h"
14 #include "parser/Tokenizer.h"
15 #include "Parsing.h"
16 #include "security/PeerOptions.h"
17 
18 #if USE_OPENSSL
19 #include "ssl/support.h"
20 #endif
21 
22 #include <bitset>
23 
26 {
27  static const auto peerOptions = new PeerOptions();
28  return *peerOptions;
29 }
30 
32 {
33  // init options consistent with an empty sslOptions
34  parseOptions();
35 }
36 
37 void
38 Security::PeerOptions::parse(const char *token)
39 {
40  if (!*token) {
41  // config says just "ssl" or "tls" (or "tls-")
42  encryptTransport = true;
43  return;
44  }
45 
46  if (strncmp(token, "disable", 7) == 0) {
47  clear();
48  return;
49  }
50 
51  if (strncmp(token, "cert=", 5) == 0) {
52  KeyData t;
53  t.privateKeyFile = t.certFile = SBuf(token + 5);
54  certs.emplace_back(t);
55  } else if (strncmp(token, "key=", 4) == 0) {
56  if (certs.empty() || certs.back().certFile.isEmpty()) {
57  fatal("cert= option must be set before key= is used.");
58  return;
59  }
60  KeyData &t = certs.back();
61  t.privateKeyFile = SBuf(token + 4);
62  } else if (strncmp(token, "version=", 8) == 0) {
63  debugs(0, DBG_PARSE_NOTE(1), "WARNING: UPGRADE: SSL version= is deprecated. Use options= and tls-min-version= to limit protocols instead.");
64  sslVersion = xatoi(token + 8);
65  } else if (strncmp(token, "min-version=", 12) == 0) {
66  tlsMinVersion = SBuf(token + 12);
67  optsReparse = true;
68  } else if (strncmp(token, "options=", 8) == 0) {
69  sslOptions = SBuf(token + 8);
70  optsReparse = true;
71  } else if (strncmp(token, "cipher=", 7) == 0) {
72  sslCipher = SBuf(token + 7);
73  } else if (strncmp(token, "cafile=", 7) == 0) {
74  caFiles.emplace_back(SBuf(token + 7));
75  } else if (strncmp(token, "capath=", 7) == 0) {
76  caDir = SBuf(token + 7);
77 #if !USE_OPENSSL
78  debugs(3, DBG_PARSE_NOTE(1), "WARNING: capath= option requires --with-openssl.");
79 #endif
80  } else if (strncmp(token, "crlfile=", 8) == 0) {
81  crlFile = SBuf(token + 8);
82  loadCrlFile();
83  } else if (strncmp(token, "flags=", 6) == 0) {
84  if (parsedFlags != 0) {
85  debugs(3, DBG_PARSE_NOTE(1), "WARNING: Overwriting flags=" << sslFlags << " with " << SBuf(token + 6));
86  }
87  sslFlags = SBuf(token + 6);
88  parsedFlags = parseFlags();
89  } else if (strncmp(token, "default-ca=off", 14) == 0 || strncmp(token, "no-default-ca", 13) == 0) {
90  if (flags.tlsDefaultCa.configured() && flags.tlsDefaultCa)
91  fatalf("ERROR: previous default-ca settings conflict with %s", token);
92  flags.tlsDefaultCa.configure(false);
93  } else if (strncmp(token, "default-ca=on", 13) == 0 || strncmp(token, "default-ca", 10) == 0) {
94  if (flags.tlsDefaultCa.configured() && !flags.tlsDefaultCa)
95  fatalf("ERROR: previous default-ca settings conflict with %s", token);
96  flags.tlsDefaultCa.configure(true);
97  } else if (strncmp(token, "domain=", 7) == 0) {
98  sslDomain = SBuf(token + 7);
99  } else if (strncmp(token, "no-npn", 6) == 0) {
100  flags.tlsNpn = false;
101  } else {
102  debugs(3, DBG_CRITICAL, "ERROR: Unknown TLS option '" << token << "'");
103  return;
104  }
105 
106  encryptTransport = true;
107 }
108 
109 void
110 Security::PeerOptions::dumpCfg(std::ostream &os, const char *pfx) const
111 {
112  if (!encryptTransport) {
113  os << ' ' << pfx << "disable";
114  return; // no other settings are relevant
115  }
116 
117  for (auto &i : certs) {
118  if (!i.certFile.isEmpty())
119  os << ' ' << pfx << "cert=" << i.certFile;
120 
121  if (!i.privateKeyFile.isEmpty() && i.privateKeyFile != i.certFile)
122  os << ' ' << pfx << "key=" << i.privateKeyFile;
123  }
124 
125  if (!sslOptions.isEmpty())
126  os << ' ' << pfx << "options=" << sslOptions;
127 
128  if (!sslCipher.isEmpty())
129  os << ' ' << pfx << "cipher=" << sslCipher;
130 
131  for (auto i : caFiles) {
132  os << ' ' << pfx << "cafile=" << i;
133  }
134 
135  if (!caDir.isEmpty())
136  os << ' ' << pfx << "capath=" << caDir;
137 
138  if (!crlFile.isEmpty())
139  os << ' ' << pfx << "crlfile=" << crlFile;
140 
141  if (!sslFlags.isEmpty())
142  os << ' ' << pfx << "flags=" << sslFlags;
143 
144  if (flags.tlsDefaultCa.configured()) {
145  // default ON for peers / upstream servers
146  // default OFF for listening ports
147  if (flags.tlsDefaultCa)
148  os << ' ' << pfx << "default-ca";
149  else
150  os << ' ' << pfx << "default-ca=off";
151  }
152 
153  if (!flags.tlsNpn)
154  os << ' ' << pfx << "no-npn";
155 }
156 
157 void
159 {
160  if (!tlsMinVersion.isEmpty()) {
161  ::Parser::Tokenizer tok(tlsMinVersion);
162  int64_t v = 0;
163  tlsMinOptions.clear();
164  if (tok.skip('1') && tok.skip('.') && tok.int64(v, 10, false, 1) && v <= 3) {
165  // only account for TLS here - SSL versions are handled by options= parameter
166  // avoid affecting options= parameter in cachemgr config report
167  SBuf add;
168 #if USE_OPENSSL
169  if (v > 0)
170  add.append(":NO_TLSv1");
171  if (v > 1)
172  add.append(":NO_TLSv1_1");
173  if (v > 2)
174  add.append(":NO_TLSv1_2");
175 #elif HAVE_LIBGNUTLS
176  if (v > 0)
177  add.append(":-VERS-TLS1.0");
178  if (v > 1)
179  add.append(":-VERS-TLS1.1");
180  if (v > 2)
181  add.append(":-VERS-TLS1.2");
182 #endif
183 
184  if (!tlsMinOptions.isEmpty())
185  add.chop(1); // remove the initial ':'
186  tlsMinOptions.append(add);
187  optsReparse = true;
188 
189  } else {
190  debugs(0, DBG_PARSE_NOTE(1), "WARNING: Unknown TLS minimum version: " << tlsMinVersion);
191  }
192 
193  return;
194  }
195 
196  if (sslVersion > 2) {
197  // backward compatibility hack for sslversion= configuration
198  // only use if tls-min-version=N.N is not present
199  // values 0-2 for auto and SSLv2 are not supported any longer.
200  // Do it this way so we DO cause changes to options= in cachemgr config report
201  const char *add = nullptr;
202  switch (sslVersion) {
203  case 3:
204 #if USE_OPENSSL
205  add = ":NO_TLSv1:NO_TLSv1_1:NO_TLSv1_2:NO_TLSv1_3";
206 #elif HAVE_LIBGNUTLS
207  add = ":-VERS-TLS1.0:-VERS-TLS1.1:-VERS-TLS1.2:-VERS-TLS1.3";
208 #endif
209  break;
210  case 4:
211 #if USE_OPENSSL
212  add = ":NO_SSLv3:NO_TLSv1_1:NO_TLSv1_2:NO_TLSv1_3";
213 #elif HAVE_LIBGNUTLS
214  add = ":+VERS-TLS1.0:-VERS-TLS1.1:-VERS-TLS1.2:-VERS-TLS1.3";
215 #endif
216  break;
217  case 5:
218 #if USE_OPENSSL
219  add = ":NO_SSLv3:NO_TLSv1:NO_TLSv1_2:NO_TLSv1_3";
220 #elif HAVE_LIBGNUTLS
221  add = ":-VERS-TLS1.0:+VERS-TLS1.1:-VERS-TLS1.2:-VERS-TLS1.3";
222 #endif
223  break;
224  case 6:
225 #if USE_OPENSSL
226  add = ":NO_SSLv3:NO_TLSv1:NO_TLSv1_1:NO_TLSv1_3";
227 #elif HAVE_LIBGNUTLS
228  add = ":-VERS-TLS1.0:-VERS-TLS1.1:-VERS-TLS1.3";
229 #endif
230  break;
231  default: // nothing
232  break;
233  }
234  if (add) {
235  if (sslOptions.isEmpty())
236  sslOptions.append(add+1, strlen(add+1));
237  else
238  sslOptions.append(add, strlen(add));
239  optsReparse = true;
240  }
241  sslVersion = 0; // prevent sslOptions being repeatedly appended
242  }
243 }
244 
247 {
249 #if USE_OPENSSL
250  Ssl::Initialize();
251 
252  SSL_CTX *t = SSL_CTX_new(TLS_client_method());
253  if (!t) {
254  const auto x = ERR_get_error();
255  fatalf("Failed to allocate TLS client context: %s\n", Security::ErrorString(x));
256  }
257  ctx = convertContextFromRawPtr(t);
258 
259 #elif HAVE_LIBGNUTLS
260  // Initialize for X.509 certificate exchange
261  gnutls_certificate_credentials_t t;
262  if (const auto x = gnutls_certificate_allocate_credentials(&t)) {
263  fatalf("Failed to allocate TLS client context: %s\n", Security::ErrorString(x));
264  }
265  ctx = convertContextFromRawPtr(t);
266 
267 #else
268  debugs(83, 1, "WARNING: Failed to allocate TLS client context: No TLS library");
269 
270 #endif
271 
272  return ctx;
273 }
274 
277 {
278  updateTlsVersionLimits();
279 
280  Security::ContextPointer t(createBlankContext());
281  if (t) {
282  if (setOptions)
283  updateContextOptions(t);
284 #if USE_OPENSSL
285  // XXX: temporary performance regression. c_str() data copies and prevents this being a const method
286  Ssl::InitClientContext(t, *this, parsedFlags);
287 #endif
288  updateContextNpn(t);
289  updateContextCa(t);
290  updateContextCrl(t);
291  updateContextTrust(t);
292  }
293 
294  return t;
295 }
296 
297 #if USE_OPENSSL
298 static struct ssl_option {
300  const char *name;
302 
303 } ssl_options[] = {
304 
305 #if defined(SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG)
306  {
307  "NETSCAPE_REUSE_CIPHER_CHANGE_BUG", SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG
308  },
309 #endif
310 #if defined(SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG)
311  {
312  "SSLREF2_REUSE_CERT_TYPE_BUG", SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG
313  },
314 #endif
315 #if defined(SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER)
316  {
317  "MICROSOFT_BIG_SSLV3_BUFFER", SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER
318  },
319 #endif
320 #if defined(SSL_OP_SSLEAY_080_CLIENT_DH_BUG)
321  {
322  "SSLEAY_080_CLIENT_DH_BUG", SSL_OP_SSLEAY_080_CLIENT_DH_BUG
323  },
324 #endif
325 #if defined(SSL_OP_TLS_D5_BUG)
326  {
327  "TLS_D5_BUG", SSL_OP_TLS_D5_BUG
328  },
329 #endif
330 #if defined(SSL_OP_TLS_BLOCK_PADDING_BUG)
331  {
332  "TLS_BLOCK_PADDING_BUG", SSL_OP_TLS_BLOCK_PADDING_BUG
333  },
334 #endif
335 #if defined(SSL_OP_TLS_ROLLBACK_BUG)
336  {
337  "TLS_ROLLBACK_BUG", SSL_OP_TLS_ROLLBACK_BUG
338  },
339 #endif
340 #if defined(SSL_OP_ALL)
341  {
342  "ALL", SSL_OP_ALL
343  },
344 #endif
345 #if defined(SSL_OP_SINGLE_DH_USE)
346  {
347  "SINGLE_DH_USE", SSL_OP_SINGLE_DH_USE
348  },
349 #endif
350 #if defined(SSL_OP_EPHEMERAL_RSA)
351  {
352  "EPHEMERAL_RSA", SSL_OP_EPHEMERAL_RSA
353  },
354 #endif
355 #if defined(SSL_OP_PKCS1_CHECK_1)
356  {
357  "PKCS1_CHECK_1", SSL_OP_PKCS1_CHECK_1
358  },
359 #endif
360 #if defined(SSL_OP_PKCS1_CHECK_2)
361  {
362  "PKCS1_CHECK_2", SSL_OP_PKCS1_CHECK_2
363  },
364 #endif
365 #if defined(SSL_OP_NETSCAPE_CA_DN_BUG)
366  {
367  "NETSCAPE_CA_DN_BUG", SSL_OP_NETSCAPE_CA_DN_BUG
368  },
369 #endif
370 #if defined(SSL_OP_NON_EXPORT_FIRST)
371  {
372  "NON_EXPORT_FIRST", SSL_OP_NON_EXPORT_FIRST
373  },
374 #endif
375 #if defined(SSL_OP_CIPHER_SERVER_PREFERENCE)
376  {
377  "CIPHER_SERVER_PREFERENCE", SSL_OP_CIPHER_SERVER_PREFERENCE
378  },
379 #endif
380 #if defined(SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG)
381  {
382  "NETSCAPE_DEMO_CIPHER_CHANGE_BUG", SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG
383  },
384 #endif
385 #if defined(SSL_OP_NO_SSLv3)
386  {
387  "NO_SSLv3", SSL_OP_NO_SSLv3
388  },
389 #endif
390 #if defined(SSL_OP_NO_TLSv1)
391  {
392  "NO_TLSv1", SSL_OP_NO_TLSv1
393  },
394 #else
395  { "NO_TLSv1", 0 },
396 #endif
397 #if defined(SSL_OP_NO_TLSv1_1)
398  {
399  "NO_TLSv1_1", SSL_OP_NO_TLSv1_1
400  },
401 #else
402  { "NO_TLSv1_1", 0 },
403 #endif
404 #if defined(SSL_OP_NO_TLSv1_2)
405  {
406  "NO_TLSv1_2", SSL_OP_NO_TLSv1_2
407  },
408 #else
409  { "NO_TLSv1_2", 0 },
410 #endif
411 #if defined(SSL_OP_NO_TLSv1_3)
412  {
413  "NO_TLSv1_3", SSL_OP_NO_TLSv1_3
414  },
415 #else
416  { "NO_TLSv1_3", 0 },
417 #endif
418 #if defined(SSL_OP_NO_COMPRESSION)
419  {
420  "No_Compression", SSL_OP_NO_COMPRESSION
421  },
422 #endif
423 #if defined(SSL_OP_NO_TICKET)
424  {
425  "NO_TICKET", SSL_OP_NO_TICKET
426  },
427 #endif
428 #if defined(SSL_OP_SINGLE_ECDH_USE)
429  {
430  "SINGLE_ECDH_USE", SSL_OP_SINGLE_ECDH_USE
431  },
432 #endif
433  {
434  "", 0
435  },
436  {
437  nullptr, 0
438  }
439 };
440 #endif /* USE_OPENSSL */
441 
446 void
448 {
449  // do not allow repeated parsing when multiple contexts are created
450  // NP: we cannot use !parsedOptions because a nil value does have meaning there
451  if (!optsReparse)
452  return;
453  optsReparse = false;
454 
455  // combination of settings we have to set via parsedOptions.
456  // options= with override by tls-min-version=
457  SBuf str;
458  str.append(sslOptions);
459  str.append(tlsMinOptions);
460 
461 #if USE_OPENSSL
463  ParsedOptions op = 0;
464 
465  while (!tok.atEnd()) {
466  enum {
467  MODE_ADD, MODE_REMOVE
468  } mode;
469 
470  if (tok.skip('-') || tok.skip('!'))
471  mode = MODE_REMOVE;
472  else {
473  (void)tok.skip('+'); // default action is add. ignore if missing operator
474  mode = MODE_ADD;
475  }
476 
477  static const CharacterSet optChars = CharacterSet("TLS-option", "_") + CharacterSet::ALPHA + CharacterSet::DIGIT;
478  int64_t hex = 0;
479  SBuf option;
480  ParsedOptions value = 0;
481  bool found = false;
482 
483  // Bug 4429: identify the full option name before determining text or numeric
484  if (tok.prefix(option, optChars)) {
485 
486  // find the named option in our supported set
487  for (struct ssl_option *opttmp = ssl_options; opttmp->name; ++opttmp) {
488  if (option.cmp(opttmp->name) == 0) {
489  value = opttmp->value;
490  found = true;
491  break;
492  }
493  }
494 
495  // Special case.. hex specification
496  ::Parser::Tokenizer tmp(option);
497  if (!found && tmp.int64(hex, 16, false) && tmp.atEnd()) {
498  value = hex;
499  found = true;
500  }
501  }
502 
503  if (value) {
504  switch (mode) {
505  case MODE_ADD:
506  op |= value;
507  break;
508  case MODE_REMOVE:
509  op &= ~value;
510  break;
511  }
512  } else {
513  debugs(83, DBG_PARSE_NOTE(DBG_IMPORTANT), "ERROR: " << (found?"Unsupported":"Unknown") << " TLS option " << option);
514  }
515 
516  static const CharacterSet delims("TLS-option-delim",":,");
517  if (!tok.skipAll(delims) && !tok.atEnd()) {
518  fatalf("Unknown TLS option '" SQUIDSBUFPH "'", SQUIDSBUFPRINT(tok.remaining()));
519  }
520 
521  }
522 
523 #if defined(SSL_OP_NO_SSLv2)
524  // compliance with RFC 6176: Prohibiting Secure Sockets Layer (SSL) Version 2.0
525  if (SSL_OP_NO_SSLv2)
526  op |= SSL_OP_NO_SSLv2;
527 #endif
528  parsedOptions = op;
529 
530 #elif HAVE_LIBGNUTLS
531  if (str.isEmpty()) {
532  parsedOptions.reset();
533  return;
534  }
535 
536  const char *err = nullptr;
537  const char *priorities = str.c_str();
538  gnutls_priority_t op;
539  const auto x = gnutls_priority_init(&op, priorities, &err);
540  if (x != GNUTLS_E_SUCCESS) {
541  fatalf("(%s) in TLS options '%s'", ErrorString(x), err);
542  }
543  parsedOptions = Security::ParsedOptions(op, [](gnutls_priority_t p) {
544  debugs(83, 5, "gnutls_priority_deinit p=" << (void*)p);
545  gnutls_priority_deinit(p);
546  });
547 #endif
548 }
549 
555 {
556  if (sslFlags.isEmpty())
557  return 0;
558 
559  static struct {
560  SBuf label;
561  ParsedPortFlags mask;
562  } flagTokens[] = {
563  { SBuf("NO_DEFAULT_CA"), SSL_FLAG_NO_DEFAULT_CA },
564  { SBuf("DELAYED_AUTH"), SSL_FLAG_DELAYED_AUTH },
565  { SBuf("DONT_VERIFY_PEER"), SSL_FLAG_DONT_VERIFY_PEER },
566  { SBuf("CONDITIONAL_AUTH"), SSL_FLAG_CONDITIONAL_AUTH },
567  { SBuf("DONT_VERIFY_DOMAIN"), SSL_FLAG_DONT_VERIFY_DOMAIN },
568  { SBuf("NO_SESSION_REUSE"), SSL_FLAG_NO_SESSION_REUSE },
569 #if X509_V_FLAG_CRL_CHECK
570  { SBuf("VERIFY_CRL"), SSL_FLAG_VERIFY_CRL },
571  { SBuf("VERIFY_CRL_ALL"), SSL_FLAG_VERIFY_CRL_ALL },
572 #endif
573  { SBuf(), 0 }
574  };
575 
576  ::Parser::Tokenizer tok(sslFlags);
577  static const CharacterSet delims("Flag-delimiter", ":,");
578 
579  ParsedPortFlags fl = 0;
580  do {
581  ParsedPortFlags found = 0;
582  for (size_t i = 0; flagTokens[i].mask; ++i) {
583  // XXX: skips FOO in FOOBAR, missing merged flags and trailing typos
584  if (tok.skip(flagTokens[i].label)) {
585  found = flagTokens[i].mask;
586  break;
587  }
588  }
589  if (!found)
590  fatalf("Unknown TLS flag '" SQUIDSBUFPH "'", SQUIDSBUFPRINT(tok.remaining()));
591  if (found == SSL_FLAG_NO_DEFAULT_CA) {
592  if (flags.tlsDefaultCa.configured() && flags.tlsDefaultCa)
593  fatal("ERROR: previous default-ca settings conflict with sslflags=NO_DEFAULT_CA");
594  debugs(83, DBG_PARSE_NOTE(2), "WARNING: flags=NO_DEFAULT_CA is deprecated. Use tls-default-ca=off instead.");
595  flags.tlsDefaultCa.configure(false);
596  } else
597  fl |= found;
598  } while (tok.skipOne(delims));
599 
600  const auto mutuallyExclusive =
604  typedef std::bitset<sizeof(decltype(fl))> ParsedPortFlagBits;
605  if (ParsedPortFlagBits(fl & mutuallyExclusive).count() > 1) {
606  if (fl & SSL_FLAG_CONDITIONAL_AUTH)
607  throw TextException("CONDITIONAL_AUTH is not compatible with NO_DEFAULT_CA and DELAYED_AUTH flags", Here());
608  debugs(83, DBG_PARSE_NOTE(DBG_IMPORTANT), "WARNING: Mixtures of incompatible TLS flags" <<
609  " are deprecated and will become a fatal configuration error");
610  }
611 
612  return fl;
613 }
614 
617 void
619 {
620  parsedCrl.clear();
621  if (crlFile.isEmpty())
622  return;
623 
624 #if USE_OPENSSL
625  BIO *in = BIO_new_file(crlFile.c_str(), "r");
626  if (!in) {
627  debugs(83, 2, "WARNING: Failed to open CRL file " << crlFile);
628  return;
629  }
630 
631  while (X509_CRL *crl = PEM_read_bio_X509_CRL(in,nullptr,nullptr,nullptr)) {
632  parsedCrl.emplace_back(Security::CrlPointer(crl));
633  }
634  BIO_free(in);
635 #endif
636 }
637 
638 void
640 {
641  parseOptions();
642 #if USE_OPENSSL
643  SSL_CTX_set_options(ctx.get(), parsedOptions);
644 #elif HAVE_LIBGNUTLS
645  // NP: GnuTLS uses 'priorities' which are set only per-session instead.
646  (void)ctx;
647 #else
648  (void)ctx;
649 #endif
650 }
651 
652 #if USE_OPENSSL && defined(TLSEXT_TYPE_next_proto_neg)
653 // Dummy next_proto_neg callback
654 static int
655 ssl_next_proto_cb(SSL *, unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, void * /* arg */)
656 {
657  static const unsigned char supported_protos[] = {8, 'h','t','t', 'p', '/', '1', '.', '1'};
658  (void)SSL_select_next_proto(out, outlen, in, inlen, supported_protos, sizeof(supported_protos));
659  return SSL_TLSEXT_ERR_OK;
660 }
661 #endif
662 
663 void
665 {
666  if (!flags.tlsNpn)
667  return;
668 
669 #if USE_OPENSSL && defined(TLSEXT_TYPE_next_proto_neg)
670  SSL_CTX_set_next_proto_select_cb(ctx.get(), &ssl_next_proto_cb, nullptr);
671 #else
672  // NOTE: GnuTLS does not support the obsolete NPN extension.
673  // it does support ALPN per-session, not per-context.
674  (void)ctx;
675 #endif
676 }
677 
678 static const char *
680 {
681  debugs(83, 8, "Setting default system Trusted CA. ctx=" << (void*)ctx.get());
682 #if USE_OPENSSL
683  if (SSL_CTX_set_default_verify_paths(ctx.get()) == 0)
684  return Security::ErrorString(ERR_get_error());
685 
686 #elif HAVE_LIBGNUTLS
687  auto x = gnutls_certificate_set_x509_system_trust(ctx.get());
688  if (x < 0)
689  return Security::ErrorString(x);
690 
691 #endif
692  return nullptr;
693 }
694 
695 void
697 {
698  debugs(83, 8, "Setting CA certificate locations.");
699 #if USE_OPENSSL
700  if (const char *path = caDir.isEmpty() ? nullptr : caDir.c_str()) {
701  if (!SSL_CTX_load_verify_locations(ctx.get(), nullptr, path)) {
702  const auto x = ERR_get_error();
703  debugs(83, DBG_IMPORTANT, "WARNING: Ignoring error setting CA certificate location " << path << ": " << Security::ErrorString(x));
704  }
705  }
706 #endif
707  for (auto i : caFiles) {
708 #if USE_OPENSSL
709  if (!SSL_CTX_load_verify_locations(ctx.get(), i.c_str(), nullptr)) {
710  const auto x = ERR_get_error();
711  debugs(83, DBG_IMPORTANT, "WARNING: Ignoring error setting CA certificate location " <<
712  i << ": " << Security::ErrorString(x));
713  }
714 #elif HAVE_LIBGNUTLS
715  const auto x = gnutls_certificate_set_x509_trust_file(ctx.get(), i.c_str(), GNUTLS_X509_FMT_PEM);
716  if (x < 0) {
717  debugs(83, DBG_IMPORTANT, "WARNING: Ignoring error setting CA certificate location " <<
718  i << ": " << Security::ErrorString(x));
719  }
720 #endif
721  }
722 
723  if (!flags.tlsDefaultCa)
724  return;
725 
726  if (const char *err = loadSystemTrustedCa(ctx)) {
727  debugs(83, DBG_IMPORTANT, "WARNING: Ignoring error setting default trusted CA : " << err);
728  }
729 }
730 
731 void
733 {
734 #if USE_OPENSSL
735  bool verifyCrl = false;
736  X509_STORE *st = SSL_CTX_get_cert_store(ctx.get());
737  if (parsedCrl.size()) {
738  for (auto &i : parsedCrl) {
739  if (!X509_STORE_add_crl(st, i.get()))
740  debugs(83, 2, "WARNING: Failed to add CRL");
741  else
742  verifyCrl = true;
743  }
744  }
745 
746 #if X509_V_FLAG_CRL_CHECK
747  if ((parsedFlags & SSL_FLAG_VERIFY_CRL_ALL))
748  X509_STORE_set_flags(st, X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL);
749  else if (verifyCrl || (parsedFlags & SSL_FLAG_VERIFY_CRL))
750  X509_STORE_set_flags(st, X509_V_FLAG_CRL_CHECK);
751 #endif
752 
753 #else /* USE_OPENSSL */
754  (void)ctx;
755 #endif /* USE_OPENSSL */
756 }
757 
758 void
760 {
761 #if USE_OPENSSL
762 #if defined(X509_V_FLAG_PARTIAL_CHAIN)
763  const auto st = SSL_CTX_get_cert_store(ctx.get());
764  assert(st);
765  if (X509_STORE_set_flags(st, X509_V_FLAG_PARTIAL_CHAIN) != 1) {
766  debugs(83, DBG_IMPORTANT, "ERROR: Failed to enable trust in intermediate CA certificates: " <<
767  Security::ErrorString(ERR_get_error()));
768  }
769 #endif
770 #elif HAVE_LIBGNUTLS
771  // Modern GnuTLS versions trust intermediate CA certificates by default.
772  (void)ctx;
773 #else
774  (void)ctx;
775 #endif /* TLS library */
776 }
777 
778 void
780 {
781  parseOptions();
782 #if USE_OPENSSL
783  debugs(83, 5, "set OpenSSL options for session=" << s << ", parsedOptions=" << parsedOptions);
784  // XXX: Options already set before (via the context) are not cleared!
785  SSL_set_options(s.get(), parsedOptions);
786 
787 #elif HAVE_LIBGNUTLS
788  LibErrorCode x;
789  SBuf errMsg;
790  if (!parsedOptions) {
791  debugs(83, 5, "set GnuTLS default priority/options for session=" << s);
792  x = gnutls_set_default_priority(s.get());
793  static const SBuf defaults("default");
794  errMsg = defaults;
795  } else {
796  debugs(83, 5, "set GnuTLS session=" << s << ", options='" << sslOptions << ":" << tlsMinOptions << "'");
797  x = gnutls_priority_set(s.get(), parsedOptions.get());
798  errMsg = sslOptions;
799  }
800 
801  if (x != GNUTLS_E_SUCCESS) {
802  debugs(83, DBG_IMPORTANT, "ERROR: session=" << s << " Failed to set TLS options (" << errMsg << ":" << tlsMinVersion << "). error: " << Security::ErrorString(x));
803  }
804 #else
805  (void)s;
806 #endif
807 }
808 
809 void
811 {
812  while(const char *token = ConfigParser::NextToken())
813  opt->parse(token);
814  opt->parseOptions();
815 }
816 
void fatal(const char *message)
Definition: fatal.cc:28
#define SSL_FLAG_DONT_VERIFY_DOMAIN
Definition: forward.h:56
bool InitClientContext(Security::ContextPointer &, Security::PeerOptions &, Security::ParsedPortFlags)
initialize a TLS client context with OpenSSL specific settings
Definition: support.cc:806
void updateContextOptions(Security::ContextPointer &)
Setup the library specific 'options=' parameters for the given context.
Definition: PeerOptions.cc:639
void Initialize()
Definition: support.cc:747
#define Here()
source code location of the caller
Definition: Here.h:15
#define SSL_FLAG_NO_DEFAULT_CA
Definition: forward.h:53
#define DBG_CRITICAL
Definition: Stream.h:37
virtual Security::ContextPointer createBlankContext() const
generate an unset security context object
Definition: PeerOptions.cc:246
std::shared_ptr< SSL_CTX > ContextPointer
Definition: Context.h:29
Security::ContextPointer createClientContext(bool setOptions)
generate a security client-context from these configured options
Definition: PeerOptions.cc:276
bool isEmpty() const
Definition: SBuf.h:435
Definition: SBuf.h:93
bool atEnd() const
whether the end of the buffer has been reached
Definition: Tokenizer.h:41
uint64_t ParsedOptions
Definition: forward.h:194
#define SSL_FLAG_DELAYED_AUTH
Definition: forward.h:54
static const char * loadSystemTrustedCa(Security::ContextPointer &ctx)
Definition: PeerOptions.cc:679
static const CharacterSet ALPHA
Definition: CharacterSet.h:76
void updateTlsVersionLimits()
sync the context options with tls-min-version=N configuration
Definition: PeerOptions.cc:158
const char * name
Definition: PeerOptions.cc:300
Security::ParsedOptions value
Definition: PeerOptions.cc:301
SBuf & chop(size_type pos, size_type n=npos)
Definition: SBuf.cc:530
#define SQUIDSBUFPRINT(s)
Definition: SBuf.h:32
void parseOptions()
parse and verify the [tls-]options= string in sslOptions
Definition: PeerOptions.cc:447
#define DBG_PARSE_NOTE(x)
Definition: Stream.h:42
TLS squid.conf settings for a remote server peer.
Definition: PeerOptions.h:25
SBuf privateKeyFile
path of file containing private key in PEM format
Definition: KeyData.h:28
#define SSL_FLAG_NO_SESSION_REUSE
Definition: forward.h:57
int xatoi(const char *token)
Definition: Parsing.cc:44
long ParsedPortFlags
Definition: forward.h:204
void updateContextCrl(Security::ContextPointer &)
setup the CRL details for the given context
Definition: PeerOptions.cc:732
#define assert(EX)
Definition: assert.h:17
void fatalf(const char *fmt,...)
Definition: fatal.cc:68
virtual void dumpCfg(std::ostream &, const char *pfx) const
output squid.conf syntax with 'pfx' prefix on parameters for the stored settings
Definition: PeerOptions.cc:110
static const CharacterSet DIGIT
Definition: CharacterSet.h:84
const char * c_str()
Definition: SBuf.cc:516
static struct ssl_option ssl_options[]
#define SSL_FLAG_DONT_VERIFY_PEER
Definition: forward.h:55
SBuf & append(const SBuf &S)
Definition: SBuf.cc:185
static char * NextToken()
#define SSL_FLAG_VERIFY_CRL_ALL
Definition: forward.h:59
void updateContextNpn(Security::ContextPointer &)
setup the NPN extension details for the given context
Definition: PeerOptions.cc:664
std::shared_ptr< SSL > SessionPointer
Definition: Session.h:53
Definition: parse.c:160
int cmp(const SBuf &S, const size_type n) const
shorthand version for compare()
Definition: SBuf.h:279
bool int64(int64_t &result, int base=0, bool allowSign=true, SBuf::size_type limit=SBuf::npos)
Definition: Tokenizer.cc:238
void updateContextCa(Security::ContextPointer &)
setup the CA details for the given context
Definition: PeerOptions.cc:696
TLS certificate and private key details from squid.conf.
Definition: KeyData.h:20
an std::runtime_error with thrower location info
Definition: TextException.h:20
#define SSL_FLAG_CONDITIONAL_AUTH
Definition: forward.h:60
void updateSessionOptions(Security::SessionPointer &)
setup any library-specific options that can be set for the given session
Definition: PeerOptions.cc:779
set of options we can parse and what they map to
Definition: PeerOptions.cc:299
#define DBG_IMPORTANT
Definition: Stream.h:38
#define SSL_FLAG_VERIFY_CRL
Definition: forward.h:58
void updateContextTrust(Security::ContextPointer &)
decide which CAs to trust
Definition: PeerOptions.cc:759
optimized set of C chars, with quick membership test and merge support
Definition: CharacterSet.h:17
SBuf certFile
path of file containing PEM format X.509 certificate
Definition: KeyData.h:27
PeerOptions & ProxyOutgoingConfig()
configuration options for DIRECT server access
Definition: PeerOptions.cc:25
const char * ErrorString(const LibErrorCode code)
converts numeric LibErrorCode into a human-friendlier string
Definition: forward.h:152
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:192
ParsedPortFlags parseFlags()
Definition: PeerOptions.cc:554
#define TLS_client_method
Definition: openssl.h:196
#define SQUIDSBUFPH
Definition: SBuf.h:31
void parse_securePeerOptions(Security::PeerOptions *opt)
Definition: PeerOptions.cc:810
unsigned long LibErrorCode
TLS library-reported non-validation error.
Definition: forward.h:141
virtual void parse(const char *)
parse a TLS squid.conf option
Definition: PeerOptions.cc:38

 

Introduction

Documentation

Support

Miscellaneous