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