]> git.ipfire.org Git - thirdparty/squid.git/blob - src/security/PeerOptions.cc
Maintenance: update --with-gnutls detection (#1685)
[thirdparty/squid.git] / src / security / PeerOptions.cc
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
24 Security::PeerOptions Security::ProxyOutgoingConfig;
25
26 Security::PeerOptions::PeerOptions()
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
153 Security::PeerOptions::updateTlsVersionLimits()
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
240 Security::ContextPointer
241 Security::PeerOptions::createBlankContext() const
242 {
243 Security::ContextPointer ctx;
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
270 Security::ContextPointer
271 Security::PeerOptions::createClientContext(bool setOptions)
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 /// set of options we can parse and what they map to
294 static struct ssl_option {
295 const char *name;
296 Security::ParsedOptions value;
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
437 /**
438 * Pre-parse TLS options= parameter to be applied when the TLS objects created.
439 * Options must not used in the case of peek or stare bump mode.
440 */
441 void
442 Security::PeerOptions::parseOptions()
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
457 ::Parser::Tokenizer tok(str);
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
545 /**
546 * Parses the TLS flags squid.conf parameter
547 */
548 Security::ParsedPortFlags
549 Security::PeerOptions::parseFlags()
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 =
596 SSL_FLAG_DONT_VERIFY_PEER|
597 SSL_FLAG_DELAYED_AUTH|
598 SSL_FLAG_CONDITIONAL_AUTH;
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
610 /// Load a CRLs list stored in the file whose /path/name is in crlFile
611 /// replaces any CRL loaded previously
612 void
613 Security::PeerOptions::loadCrlFile()
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
634 Security::PeerOptions::updateContextOptions(Security::ContextPointer &ctx)
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
659 Security::PeerOptions::updateContextNpn(Security::ContextPointer &ctx)
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 *
674 loadSystemTrustedCa(Security::ContextPointer &ctx)
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
691 Security::PeerOptions::updateContextCa(Security::ContextPointer &ctx)
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
727 Security::PeerOptions::updateContextCrl(Security::ContextPointer &ctx)
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
754 Security::PeerOptions::updateContextTrust(Security::ContextPointer &ctx)
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
774 Security::PeerOptions::updateSessionOptions(Security::SessionPointer &s)
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
805 parse_securePeerOptions(Security::PeerOptions *opt)
806 {
807 while(const char *token = ConfigParser::NextToken())
808 opt->parse(token);
809 opt->parseOptions();
810 }
811