Squid Web Cache master
Loading...
Searching...
No Matches
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"
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("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) {
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
617void
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
638void
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
654static int
655ssl_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
663void
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
678static 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
695void
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
731void
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
758void
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
778void
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
809void
811{
812 while(const char *token = ConfigParser::NextToken())
813 opt->parse(token);
814 opt->parseOptions();
815}
816
#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:806
void Initialize()
Definition support.cc:747
#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
#define SSL_FLAG_DELAYED_AUTH
Definition forward.h:54
set of options we can parse and what they map to
const char * name
Security::ParsedOptions value
Definition parse.c:160