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