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

 

Introduction

Documentation

Support

Miscellaneous