Squid Web Cache master
Loading...
Searching...
No Matches
PeerOptions.cc
Go to the documentation of this file.
1/*
2 * Copyright (C) 1996-2026 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"
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
35}
36
37void
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
109void
110Security::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
157void
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
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
299static 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 }
440#endif /* USE_OPENSSL */
441
446void
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("DONT_VERIFY_PEER"), SSL_FLAG_DONT_VERIFY_PEER },
565 { SBuf("CONDITIONAL_AUTH"), SSL_FLAG_CONDITIONAL_AUTH },
566 { SBuf("DONT_VERIFY_DOMAIN"), SSL_FLAG_DONT_VERIFY_DOMAIN },
567 { SBuf("NO_SESSION_REUSE"), SSL_FLAG_NO_SESSION_REUSE },
568#if X509_V_FLAG_CRL_CHECK
569 { SBuf("VERIFY_CRL"), SSL_FLAG_VERIFY_CRL },
570 { SBuf("VERIFY_CRL_ALL"), SSL_FLAG_VERIFY_CRL_ALL },
571#endif
572 { SBuf(), 0 }
573 };
574
575 ::Parser::Tokenizer tok(sslFlags);
576 static const CharacterSet delims("Flag-delimiter", ":,");
577
578 ParsedPortFlags fl = 0;
579 do {
580 ParsedPortFlags found = 0;
581 for (size_t i = 0; flagTokens[i].mask; ++i) {
582 // XXX: skips FOO in FOOBAR, missing merged flags and trailing typos
583 if (tok.skip(flagTokens[i].label)) {
584 found = flagTokens[i].mask;
585 break;
586 }
587 }
588 if (!found)
589 fatalf("Unknown TLS flag '" SQUIDSBUFPH "'", SQUIDSBUFPRINT(tok.remaining()));
590 if (found == SSL_FLAG_NO_DEFAULT_CA) {
591 if (flags.tlsDefaultCa.configured() && flags.tlsDefaultCa)
592 fatal("ERROR: previous default-ca settings conflict with sslflags=NO_DEFAULT_CA");
593 debugs(83, DBG_PARSE_NOTE(2), "WARNING: flags=NO_DEFAULT_CA is deprecated. Use tls-default-ca=off instead.");
594 flags.tlsDefaultCa.configure(false);
595 } else
596 fl |= found;
597 } while (tok.skipOne(delims));
598
599 const auto mutuallyExclusive =
602 typedef std::bitset<sizeof(decltype(fl))> ParsedPortFlagBits;
603 if (ParsedPortFlagBits(fl & mutuallyExclusive).count() > 1) {
605 throw TextException("CONDITIONAL_AUTH is not compatible with NO_DEFAULT_CA", Here());
606 debugs(83, DBG_PARSE_NOTE(DBG_IMPORTANT), "WARNING: Mixtures of incompatible TLS flags" <<
607 " are deprecated and will become a fatal configuration error");
608 }
609
610 return fl;
611}
612
615void
617{
618 parsedCrl.clear();
619 if (crlFile.isEmpty())
620 return;
621
622#if USE_OPENSSL
623 BIO *in = BIO_new_file(crlFile.c_str(), "r");
624 if (!in) {
625 debugs(83, 2, "WARNING: Failed to open CRL file " << crlFile);
626 return;
627 }
628
629 while (X509_CRL *crl = PEM_read_bio_X509_CRL(in,nullptr,nullptr,nullptr)) {
630 parsedCrl.emplace_back(Security::CrlPointer(crl));
631 }
632 BIO_free(in);
633#endif
634}
635
636void
638{
639 parseOptions();
640#if USE_OPENSSL
641 SSL_CTX_set_options(ctx.get(), parsedOptions);
642#elif HAVE_LIBGNUTLS
643 // NP: GnuTLS uses 'priorities' which are set only per-session instead.
644 (void)ctx;
645#else
646 (void)ctx;
647#endif
648}
649
650#if USE_OPENSSL && defined(TLSEXT_TYPE_next_proto_neg)
651// Dummy next_proto_neg callback
652static int
653ssl_next_proto_cb(SSL *, unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, void * /* arg */)
654{
655 static const unsigned char supported_protos[] = {8, 'h','t','t', 'p', '/', '1', '.', '1'};
656 (void)SSL_select_next_proto(out, outlen, in, inlen, supported_protos, sizeof(supported_protos));
657 return SSL_TLSEXT_ERR_OK;
658}
659#endif
660
661void
663{
664 if (!flags.tlsNpn)
665 return;
666
667#if USE_OPENSSL && defined(TLSEXT_TYPE_next_proto_neg)
668 SSL_CTX_set_next_proto_select_cb(ctx.get(), &ssl_next_proto_cb, nullptr);
669#else
670 // NOTE: GnuTLS does not support the obsolete NPN extension.
671 // it does support ALPN per-session, not per-context.
672 (void)ctx;
673#endif
674}
675
676static const char *
678{
679 debugs(83, 8, "Setting default system Trusted CA. ctx=" << (void*)ctx.get());
680#if USE_OPENSSL
681 if (SSL_CTX_set_default_verify_paths(ctx.get()) == 0)
682 return Security::ErrorString(ERR_get_error());
683
684#elif HAVE_LIBGNUTLS
685 auto x = gnutls_certificate_set_x509_system_trust(ctx.get());
686 if (x < 0)
687 return Security::ErrorString(x);
688
689#endif
690 return nullptr;
691}
692
693void
695{
696 debugs(83, 8, "Setting CA certificate locations.");
697#if USE_OPENSSL
698 if (const char *path = caDir.isEmpty() ? nullptr : caDir.c_str()) {
699 if (!SSL_CTX_load_verify_locations(ctx.get(), nullptr, path)) {
700 const auto x = ERR_get_error();
701 debugs(83, DBG_IMPORTANT, "WARNING: Ignoring error setting CA certificate location " << path << ": " << Security::ErrorString(x));
702 }
703 }
704#endif
705 for (auto i : caFiles) {
706#if USE_OPENSSL
707 if (!SSL_CTX_load_verify_locations(ctx.get(), i.c_str(), nullptr)) {
708 const auto x = ERR_get_error();
709 debugs(83, DBG_IMPORTANT, "WARNING: Ignoring error setting CA certificate location " <<
710 i << ": " << Security::ErrorString(x));
711 }
712#elif HAVE_LIBGNUTLS
713 const auto x = gnutls_certificate_set_x509_trust_file(ctx.get(), i.c_str(), GNUTLS_X509_FMT_PEM);
714 if (x < 0) {
715 debugs(83, DBG_IMPORTANT, "WARNING: Ignoring error setting CA certificate location " <<
716 i << ": " << Security::ErrorString(x));
717 }
718#endif
719 }
720
721 if (!flags.tlsDefaultCa)
722 return;
723
724 if (const char *err = loadSystemTrustedCa(ctx)) {
725 debugs(83, DBG_IMPORTANT, "WARNING: Ignoring error setting default trusted CA : " << err);
726 }
727}
728
729void
731{
732#if USE_OPENSSL
733 bool verifyCrl = false;
734 X509_STORE *st = SSL_CTX_get_cert_store(ctx.get());
735 if (parsedCrl.size()) {
736 for (auto &i : parsedCrl) {
737 if (!X509_STORE_add_crl(st, i.get()))
738 debugs(83, 2, "WARNING: Failed to add CRL");
739 else
740 verifyCrl = true;
741 }
742 }
743
744#if X509_V_FLAG_CRL_CHECK
745 if ((parsedFlags & SSL_FLAG_VERIFY_CRL_ALL))
746 X509_STORE_set_flags(st, X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL);
747 else if (verifyCrl || (parsedFlags & SSL_FLAG_VERIFY_CRL))
748 X509_STORE_set_flags(st, X509_V_FLAG_CRL_CHECK);
749#endif
750
751#else /* USE_OPENSSL */
752 (void)ctx;
753#endif /* USE_OPENSSL */
754}
755
756void
758{
759#if USE_OPENSSL
760#if defined(X509_V_FLAG_PARTIAL_CHAIN)
761 const auto st = SSL_CTX_get_cert_store(ctx.get());
762 assert(st);
763 if (X509_STORE_set_flags(st, X509_V_FLAG_PARTIAL_CHAIN) != 1) {
764 debugs(83, DBG_IMPORTANT, "ERROR: Failed to enable trust in intermediate CA certificates: " <<
765 Security::ErrorString(ERR_get_error()));
766 }
767#endif
768#elif HAVE_LIBGNUTLS
769 // Modern GnuTLS versions trust intermediate CA certificates by default.
770 (void)ctx;
771#else
772 (void)ctx;
773#endif /* TLS library */
774}
775
776void
778{
779 parseOptions();
780#if USE_OPENSSL
781 debugs(83, 5, "set OpenSSL options for session=" << s << ", parsedOptions=" << parsedOptions);
782 // XXX: Options already set before (via the context) are not cleared!
783 SSL_set_options(s.get(), parsedOptions);
784
785#elif HAVE_LIBGNUTLS
786 LibErrorCode x;
787 SBuf errMsg;
788 if (!parsedOptions) {
789 debugs(83, 5, "set GnuTLS default priority/options for session=" << s);
790 x = gnutls_set_default_priority(s.get());
791 static const SBuf defaults("default");
792 errMsg = defaults;
793 } else {
794 debugs(83, 5, "set GnuTLS session=" << s << ", options='" << sslOptions << ":" << tlsMinOptions << "'");
795 x = gnutls_priority_set(s.get(), parsedOptions.get());
796 errMsg = sslOptions;
797 }
798
799 if (x != GNUTLS_E_SUCCESS) {
800 debugs(83, DBG_IMPORTANT, "ERROR: session=" << s << " Failed to set TLS options (" << errMsg << ":" << tlsMinVersion << "). error: " << Security::ErrorString(x));
801 }
802#else
803 (void)s;
804#endif
805}
806
807void
809{
810 while(const char *token = ConfigParser::NextToken())
811 opt->parse(token);
812 opt->parseOptions();
813}
814
#define Here()
source code location of the caller
Definition Here.h:15
int xatoi(const char *token)
Definition Parsing.cc:44
void parse_securePeerOptions(Security::PeerOptions *opt)
static const char * loadSystemTrustedCa(Security::ContextPointer &ctx)
static struct ssl_option ssl_options[]
#define SQUIDSBUFPH
Definition SBuf.h:31
#define SQUIDSBUFPRINT(s)
Definition SBuf.h:32
#define assert(EX)
Definition assert.h:17
optimized set of C chars, with quick membership test and merge support
static const CharacterSet DIGIT
static const CharacterSet ALPHA
static char * NextToken()
bool int64(int64_t &result, int base=0, bool allowSign=true, SBuf::size_type limit=SBuf::npos)
Definition Tokenizer.cc:238
bool atEnd() const
whether the end of the buffer has been reached
Definition Tokenizer.h:41
Definition SBuf.h:94
const char * c_str()
Definition SBuf.cc:516
SBuf & chop(size_type pos, size_type n=npos)
Definition SBuf.cc:530
int cmp(const SBuf &S, const size_type n) const
shorthand version for compare()
Definition SBuf.h:279
bool isEmpty() const
Definition SBuf.h:435
SBuf & append(const SBuf &S)
Definition SBuf.cc:185
TLS certificate and private key details from squid.conf.
Definition KeyData.h:21
SBuf certFile
path of file containing PEM format X.509 certificate
Definition KeyData.h:27
SBuf privateKeyFile
path of file containing private key in PEM format
Definition KeyData.h:28
TLS squid.conf settings for a remote server peer.
Definition PeerOptions.h:26
void updateContextCrl(Security::ContextPointer &)
setup the CRL details for the given context
ParsedPortFlags parseFlags()
Security::ContextPointer createClientContext(bool setOptions)
generate a security client-context from these configured options
virtual void parse(const char *)
parse a TLS squid.conf option
void updateContextCa(Security::ContextPointer &)
setup the CA details for the given context
void updateContextOptions(Security::ContextPointer &)
Setup the library specific 'options=' parameters for the given context.
void updateTlsVersionLimits()
sync the context options with tls-min-version=N configuration
virtual void dumpCfg(std::ostream &, const char *pfx) const
output squid.conf syntax with 'pfx' prefix on parameters for the stored settings
void updateContextTrust(Security::ContextPointer &)
decide which CAs to trust
void parseOptions()
parse and verify the [tls-]options= string in sslOptions
void updateContextNpn(Security::ContextPointer &)
setup the NPN extension details for the given context
virtual Security::ContextPointer createBlankContext() const
generate an unset security context object
void updateSessionOptions(Security::SessionPointer &)
setup any library-specific options that can be set for the given session
an std::runtime_error with thrower location info
#define DBG_PARSE_NOTE(x)
Definition Stream.h:42
#define DBG_IMPORTANT
Definition Stream.h:38
#define debugs(SECTION, LEVEL, CONTENT)
Definition Stream.h:192
#define DBG_CRITICAL
Definition Stream.h:37
void fatal(const char *message)
Definition fatal.cc:28
void fatalf(const char *fmt,...)
Definition fatal.cc:68
std::shared_ptr< SSL_CTX > ContextPointer
Definition Context.h:29
uint64_t ParsedOptions
Definition forward.h:194
std::shared_ptr< SSL > SessionPointer
Definition Session.h:53
unsigned long LibErrorCode
TLS library-reported non-validation error.
Definition forward.h:141
long ParsedPortFlags
Definition forward.h:204
const char * ErrorString(const LibErrorCode code)
converts numeric LibErrorCode into a human-friendlier string
Definition forward.h:152
PeerOptions & ProxyOutgoingConfig()
configuration options for DIRECT server access
bool InitClientContext(Security::ContextPointer &, Security::PeerOptions &, Security::ParsedPortFlags)
initialize a TLS client context with OpenSSL specific settings
Definition support.cc:802
void Initialize()
Definition support.cc:743
#define TLS_client_method
Definition openssl.h:196
#define SSL_FLAG_CONDITIONAL_AUTH
Definition forward.h:60
#define SSL_FLAG_DONT_VERIFY_PEER
Definition forward.h:55
#define SSL_FLAG_NO_DEFAULT_CA
Definition forward.h:53
#define SSL_FLAG_VERIFY_CRL
Definition forward.h:58
#define SSL_FLAG_NO_SESSION_REUSE
Definition forward.h:57
#define SSL_FLAG_DONT_VERIFY_DOMAIN
Definition forward.h:56
#define SSL_FLAG_VERIFY_CRL_ALL
Definition forward.h:59
set of options we can parse and what they map to
const char * name
Security::ParsedOptions value
Definition parse.c:160