]> git.ipfire.org Git - thirdparty/squid.git/blob - src/security/PeerOptions.cc
Detail client closures of CONNECT tunnels during TLS handshake (#691)
[thirdparty/squid.git] / src / security / PeerOptions.cc
1 /*
2 * Copyright (C) 1996-2020 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.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), "UPGRADE WARNING: 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(Packable *p, const char *pfx) const
106 {
107 if (!encryptTransport) {
108 p->appendf(" %sdisable", pfx);
109 return; // no other settings are relevant
110 }
111
112 for (auto &i : certs) {
113 if (!i.certFile.isEmpty())
114 p->appendf(" %scert=" SQUIDSBUFPH, pfx, SQUIDSBUFPRINT(i.certFile));
115
116 if (!i.privateKeyFile.isEmpty() && i.privateKeyFile != i.certFile)
117 p->appendf(" %skey=" SQUIDSBUFPH, pfx, SQUIDSBUFPRINT(i.privateKeyFile));
118 }
119
120 if (!sslOptions.isEmpty())
121 p->appendf(" %soptions=" SQUIDSBUFPH, pfx, SQUIDSBUFPRINT(sslOptions));
122
123 if (!sslCipher.isEmpty())
124 p->appendf(" %scipher=" SQUIDSBUFPH, pfx, SQUIDSBUFPRINT(sslCipher));
125
126 for (auto i : caFiles) {
127 p->appendf(" %scafile=" SQUIDSBUFPH, pfx, SQUIDSBUFPRINT(i));
128 }
129
130 if (!caDir.isEmpty())
131 p->appendf(" %scapath=" SQUIDSBUFPH, pfx, SQUIDSBUFPRINT(caDir));
132
133 if (!crlFile.isEmpty())
134 p->appendf(" %scrlfile=" SQUIDSBUFPH, pfx, SQUIDSBUFPRINT(crlFile));
135
136 if (!sslFlags.isEmpty())
137 p->appendf(" %sflags=" SQUIDSBUFPH, pfx, SQUIDSBUFPRINT(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 p->appendf(" %sdefault-ca", pfx);
144 else
145 p->appendf(" %sdefault-ca=off", pfx);
146 }
147
148 if (!flags.tlsNpn)
149 p->appendf(" %sno-npn", pfx);
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 USE_GNUTLS
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 USE_GNUTLS
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 USE_GNUTLS
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 USE_GNUTLS
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 USE_GNUTLS
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 USE_GNUTLS
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 long value;
297
298 } ssl_options[] = {
299
300 #if 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 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 SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER
311 {
312 "MICROSOFT_BIG_SSLV3_BUFFER", SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER
313 },
314 #endif
315 #if 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 SSL_OP_TLS_D5_BUG
321 {
322 "TLS_D5_BUG", SSL_OP_TLS_D5_BUG
323 },
324 #endif
325 #if SSL_OP_TLS_BLOCK_PADDING_BUG
326 {
327 "TLS_BLOCK_PADDING_BUG", SSL_OP_TLS_BLOCK_PADDING_BUG
328 },
329 #endif
330 #if SSL_OP_TLS_ROLLBACK_BUG
331 {
332 "TLS_ROLLBACK_BUG", SSL_OP_TLS_ROLLBACK_BUG
333 },
334 #endif
335 #if SSL_OP_ALL
336 {
337 "ALL", (long)SSL_OP_ALL
338 },
339 #endif
340 #if SSL_OP_SINGLE_DH_USE
341 {
342 "SINGLE_DH_USE", SSL_OP_SINGLE_DH_USE
343 },
344 #endif
345 #if SSL_OP_EPHEMERAL_RSA
346 {
347 "EPHEMERAL_RSA", SSL_OP_EPHEMERAL_RSA
348 },
349 #endif
350 #if SSL_OP_PKCS1_CHECK_1
351 {
352 "PKCS1_CHECK_1", SSL_OP_PKCS1_CHECK_1
353 },
354 #endif
355 #if SSL_OP_PKCS1_CHECK_2
356 {
357 "PKCS1_CHECK_2", SSL_OP_PKCS1_CHECK_2
358 },
359 #endif
360 #if SSL_OP_NETSCAPE_CA_DN_BUG
361 {
362 "NETSCAPE_CA_DN_BUG", SSL_OP_NETSCAPE_CA_DN_BUG
363 },
364 #endif
365 #if SSL_OP_NON_EXPORT_FIRST
366 {
367 "NON_EXPORT_FIRST", SSL_OP_NON_EXPORT_FIRST
368 },
369 #endif
370 #if SSL_OP_CIPHER_SERVER_PREFERENCE
371 {
372 "CIPHER_SERVER_PREFERENCE", SSL_OP_CIPHER_SERVER_PREFERENCE
373 },
374 #endif
375 #if 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 SSL_OP_NO_SSLv3
381 {
382 "NO_SSLv3", SSL_OP_NO_SSLv3
383 },
384 #endif
385 #if SSL_OP_NO_TLSv1
386 {
387 "NO_TLSv1", SSL_OP_NO_TLSv1
388 },
389 #else
390 { "NO_TLSv1", 0 },
391 #endif
392 #if 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 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 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 SSL_OP_NO_COMPRESSION
414 {
415 "No_Compression", SSL_OP_NO_COMPRESSION
416 },
417 #endif
418 #if SSL_OP_NO_TICKET
419 {
420 "NO_TICKET", SSL_OP_NO_TICKET
421 },
422 #endif
423 #if SSL_OP_SINGLE_ECDH_USE
424 {
425 "SINGLE_ECDH_USE", SSL_OP_SINGLE_ECDH_USE
426 },
427 #endif
428 {
429 "", 0
430 },
431 {
432 NULL, 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 long 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 long value = 0;
476
477 // Bug 4429: identify the full option name before determining text or numeric
478 if (tok.prefix(option, optChars)) {
479
480 // find the named option in our supported set
481 for (struct ssl_option *opttmp = ssl_options; opttmp->name; ++opttmp) {
482 if (option.cmp(opttmp->name) == 0) {
483 value = opttmp->value;
484 break;
485 }
486 }
487
488 // Special case.. hex specification
489 ::Parser::Tokenizer tmp(option);
490 if (!value && tmp.int64(hex, 16, false) && tmp.atEnd()) {
491 value = hex;
492 }
493 }
494
495 if (value) {
496 switch (mode) {
497 case MODE_ADD:
498 op |= value;
499 break;
500 case MODE_REMOVE:
501 op &= ~value;
502 break;
503 }
504 } else {
505 debugs(83, DBG_PARSE_NOTE(1), "ERROR: Unknown TLS option " << option);
506 }
507
508 static const CharacterSet delims("TLS-option-delim",":,");
509 if (!tok.skipAll(delims) && !tok.atEnd()) {
510 fatalf("Unknown TLS option '" SQUIDSBUFPH "'", SQUIDSBUFPRINT(tok.remaining()));
511 }
512
513 }
514
515 #if SSL_OP_NO_SSLv2
516 // compliance with RFC 6176: Prohibiting Secure Sockets Layer (SSL) Version 2.0
517 op = op | SSL_OP_NO_SSLv2;
518 #endif
519 parsedOptions = op;
520
521 #elif USE_GNUTLS
522 if (str.isEmpty()) {
523 parsedOptions.reset();
524 return;
525 }
526
527 const char *err = nullptr;
528 const char *priorities = str.c_str();
529 gnutls_priority_t op;
530 const auto x = gnutls_priority_init(&op, priorities, &err);
531 if (x != GNUTLS_E_SUCCESS) {
532 fatalf("(%s) in TLS options '%s'", ErrorString(x), err);
533 }
534 parsedOptions = Security::ParsedOptions(op, [](gnutls_priority_t p) {
535 debugs(83, 5, "gnutls_priority_deinit p=" << (void*)p);
536 gnutls_priority_deinit(p);
537 });
538 #endif
539 }
540
541 /**
542 * Parses the TLS flags squid.conf parameter
543 */
544 Security::ParsedPortFlags
545 Security::PeerOptions::parseFlags()
546 {
547 if (sslFlags.isEmpty())
548 return 0;
549
550 static struct {
551 SBuf label;
552 ParsedPortFlags mask;
553 } flagTokens[] = {
554 { SBuf("NO_DEFAULT_CA"), SSL_FLAG_NO_DEFAULT_CA },
555 { SBuf("DELAYED_AUTH"), SSL_FLAG_DELAYED_AUTH },
556 { SBuf("DONT_VERIFY_PEER"), SSL_FLAG_DONT_VERIFY_PEER },
557 { SBuf("CONDITIONAL_AUTH"), SSL_FLAG_CONDITIONAL_AUTH },
558 { SBuf("DONT_VERIFY_DOMAIN"), SSL_FLAG_DONT_VERIFY_DOMAIN },
559 { SBuf("NO_SESSION_REUSE"), SSL_FLAG_NO_SESSION_REUSE },
560 #if X509_V_FLAG_CRL_CHECK
561 { SBuf("VERIFY_CRL"), SSL_FLAG_VERIFY_CRL },
562 { SBuf("VERIFY_CRL_ALL"), SSL_FLAG_VERIFY_CRL_ALL },
563 #endif
564 { SBuf(), 0 }
565 };
566
567 ::Parser::Tokenizer tok(sslFlags);
568 static const CharacterSet delims("Flag-delimiter", ":,");
569
570 ParsedPortFlags fl = 0;
571 do {
572 ParsedPortFlags found = 0;
573 for (size_t i = 0; flagTokens[i].mask; ++i) {
574 // XXX: skips FOO in FOOBAR, missing merged flags and trailing typos
575 if (tok.skip(flagTokens[i].label)) {
576 found = flagTokens[i].mask;
577 break;
578 }
579 }
580 if (!found)
581 fatalf("Unknown TLS flag '" SQUIDSBUFPH "'", SQUIDSBUFPRINT(tok.remaining()));
582 if (found == SSL_FLAG_NO_DEFAULT_CA) {
583 if (flags.tlsDefaultCa.configured() && flags.tlsDefaultCa)
584 fatal("ERROR: previous default-ca settings conflict with sslflags=NO_DEFAULT_CA");
585 debugs(83, DBG_PARSE_NOTE(2), "WARNING: flags=NO_DEFAULT_CA is deprecated. Use tls-default-ca=off instead.");
586 flags.tlsDefaultCa.configure(false);
587 } else
588 fl |= found;
589 } while (tok.skipOne(delims));
590
591 const auto mutuallyExclusive =
592 SSL_FLAG_DONT_VERIFY_PEER|
593 SSL_FLAG_DELAYED_AUTH|
594 SSL_FLAG_CONDITIONAL_AUTH;
595 typedef std::bitset<sizeof(decltype(fl))> ParsedPortFlagBits;
596 if (ParsedPortFlagBits(fl & mutuallyExclusive).count() > 1) {
597 if (fl & SSL_FLAG_CONDITIONAL_AUTH)
598 throw TextException("CONDITIONAL_AUTH is not compatible with NO_DEFAULT_CA and DELAYED_AUTH flags", Here());
599 debugs(83, DBG_PARSE_NOTE(DBG_IMPORTANT), "WARNING: Mixtures of incompatible TLS flags" <<
600 " are deprecated and will become a fatal configuration error");
601 }
602
603 return fl;
604 }
605
606 /// Load a CRLs list stored in the file whose /path/name is in crlFile
607 /// replaces any CRL loaded previously
608 void
609 Security::PeerOptions::loadCrlFile()
610 {
611 parsedCrl.clear();
612 if (crlFile.isEmpty())
613 return;
614
615 #if USE_OPENSSL
616 BIO *in = BIO_new_file(crlFile.c_str(), "r");
617 if (!in) {
618 debugs(83, 2, "WARNING: Failed to open CRL file " << crlFile);
619 return;
620 }
621
622 while (X509_CRL *crl = PEM_read_bio_X509_CRL(in,NULL,NULL,NULL)) {
623 parsedCrl.emplace_back(Security::CrlPointer(crl));
624 }
625 BIO_free(in);
626 #endif
627 }
628
629 void
630 Security::PeerOptions::updateContextOptions(Security::ContextPointer &ctx)
631 {
632 parseOptions();
633 #if USE_OPENSSL
634 SSL_CTX_set_options(ctx.get(), parsedOptions);
635 #elif USE_GNUTLS
636 // NP: GnuTLS uses 'priorities' which are set only per-session instead.
637 #endif
638 }
639
640 #if USE_OPENSSL && defined(TLSEXT_TYPE_next_proto_neg)
641 // Dummy next_proto_neg callback
642 static int
643 ssl_next_proto_cb(SSL *s, unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, void *arg)
644 {
645 static const unsigned char supported_protos[] = {8, 'h','t','t', 'p', '/', '1', '.', '1'};
646 (void)SSL_select_next_proto(out, outlen, in, inlen, supported_protos, sizeof(supported_protos));
647 return SSL_TLSEXT_ERR_OK;
648 }
649 #endif
650
651 void
652 Security::PeerOptions::updateContextNpn(Security::ContextPointer &ctx)
653 {
654 if (!flags.tlsNpn)
655 return;
656
657 #if USE_OPENSSL && defined(TLSEXT_TYPE_next_proto_neg)
658 SSL_CTX_set_next_proto_select_cb(ctx.get(), &ssl_next_proto_cb, nullptr);
659 #endif
660
661 // NOTE: GnuTLS does not support the obsolete NPN extension.
662 // it does support ALPN per-session, not per-context.
663 }
664
665 static const char *
666 loadSystemTrustedCa(Security::ContextPointer &ctx)
667 {
668 debugs(83, 8, "Setting default system Trusted CA. ctx=" << (void*)ctx.get());
669 #if USE_OPENSSL
670 if (SSL_CTX_set_default_verify_paths(ctx.get()) == 0)
671 return Security::ErrorString(ERR_get_error());
672
673 #elif USE_GNUTLS
674 auto x = gnutls_certificate_set_x509_system_trust(ctx.get());
675 if (x < 0)
676 return Security::ErrorString(x);
677
678 #endif
679 return nullptr;
680 }
681
682 void
683 Security::PeerOptions::updateContextCa(Security::ContextPointer &ctx)
684 {
685 debugs(83, 8, "Setting CA certificate locations.");
686 #if USE_OPENSSL
687 if (const char *path = caDir.isEmpty() ? nullptr : caDir.c_str()) {
688 if (!SSL_CTX_load_verify_locations(ctx.get(), nullptr, path)) {
689 const auto x = ERR_get_error();
690 debugs(83, DBG_IMPORTANT, "WARNING: Ignoring error setting CA certificate location " << path << ": " << Security::ErrorString(x));
691 }
692 }
693 #endif
694 for (auto i : caFiles) {
695 #if USE_OPENSSL
696 if (!SSL_CTX_load_verify_locations(ctx.get(), i.c_str(), nullptr)) {
697 const auto x = ERR_get_error();
698 debugs(83, DBG_IMPORTANT, "WARNING: Ignoring error setting CA certificate location " <<
699 i << ": " << Security::ErrorString(x));
700 }
701 #elif USE_GNUTLS
702 const auto x = gnutls_certificate_set_x509_trust_file(ctx.get(), i.c_str(), GNUTLS_X509_FMT_PEM);
703 if (x < 0) {
704 debugs(83, DBG_IMPORTANT, "WARNING: Ignoring error setting CA certificate location " <<
705 i << ": " << Security::ErrorString(x));
706 }
707 #endif
708 }
709
710 if (!flags.tlsDefaultCa)
711 return;
712
713 if (const char *err = loadSystemTrustedCa(ctx)) {
714 debugs(83, DBG_IMPORTANT, "WARNING: Ignoring error setting default trusted CA : " << err);
715 }
716 }
717
718 void
719 Security::PeerOptions::updateContextCrl(Security::ContextPointer &ctx)
720 {
721 #if USE_OPENSSL
722 bool verifyCrl = false;
723 X509_STORE *st = SSL_CTX_get_cert_store(ctx.get());
724 if (parsedCrl.size()) {
725 for (auto &i : parsedCrl) {
726 if (!X509_STORE_add_crl(st, i.get()))
727 debugs(83, 2, "WARNING: Failed to add CRL");
728 else
729 verifyCrl = true;
730 }
731 }
732
733 #if X509_V_FLAG_CRL_CHECK
734 if ((parsedFlags & SSL_FLAG_VERIFY_CRL_ALL))
735 X509_STORE_set_flags(st, X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL);
736 else if (verifyCrl || (parsedFlags & SSL_FLAG_VERIFY_CRL))
737 X509_STORE_set_flags(st, X509_V_FLAG_CRL_CHECK);
738 #endif
739
740 #endif /* USE_OPENSSL */
741 }
742
743 void
744 Security::PeerOptions::updateContextTrust(Security::ContextPointer &ctx)
745 {
746 #if USE_OPENSSL
747 #if defined(X509_V_FLAG_PARTIAL_CHAIN)
748 const auto st = SSL_CTX_get_cert_store(ctx.get());
749 assert(st);
750 if (X509_STORE_set_flags(st, X509_V_FLAG_PARTIAL_CHAIN) != 1) {
751 debugs(83, DBG_IMPORTANT, "ERROR: Failed to enable trust in intermediate CA certificates: " <<
752 Security::ErrorString(ERR_get_error()));
753 }
754 #endif
755 #elif USE_GNUTLS
756 // Modern GnuTLS versions trust intermediate CA certificates by default.
757 #endif /* TLS library */
758 }
759
760 void
761 Security::PeerOptions::updateSessionOptions(Security::SessionPointer &s)
762 {
763 parseOptions();
764 #if USE_OPENSSL
765 debugs(83, 5, "set OpenSSL options for session=" << s << ", parsedOptions=" << parsedOptions);
766 // XXX: Options already set before (via the context) are not cleared!
767 SSL_set_options(s.get(), parsedOptions);
768
769 #elif USE_GNUTLS
770 LibErrorCode x;
771 SBuf errMsg;
772 if (!parsedOptions) {
773 debugs(83, 5, "set GnuTLS default priority/options for session=" << s);
774 x = gnutls_set_default_priority(s.get());
775 static const SBuf defaults("default");
776 errMsg = defaults;
777 } else {
778 debugs(83, 5, "set GnuTLS session=" << s << ", options='" << sslOptions << ":" << tlsMinOptions << "'");
779 x = gnutls_priority_set(s.get(), parsedOptions.get());
780 errMsg = sslOptions;
781 }
782
783 if (x != GNUTLS_E_SUCCESS) {
784 debugs(83, DBG_IMPORTANT, "ERROR: session=" << s << " Failed to set TLS options (" << errMsg << ":" << tlsMinVersion << "). error: " << Security::ErrorString(x));
785 }
786 #endif
787 }
788
789 void
790 parse_securePeerOptions(Security::PeerOptions *opt)
791 {
792 while(const char *token = ConfigParser::NextToken())
793 opt->parse(token);
794 opt->parseOptions();
795 }
796