]> git.ipfire.org Git - thirdparty/squid.git/blame - src/security/PeerOptions.cc
Source Format Enforcement (#763)
[thirdparty/squid.git] / src / security / PeerOptions.cc
CommitLineData
9a2f63e7 1/*
f70aedc4 2 * Copyright (C) 1996-2021 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
983fab6e 22#include <bitset>
23
7e62a74f 24Security::PeerOptions Security::ProxyOutgoingConfig;
195f8adb 25
d7208dbc
AJ
26Security::PeerOptions::PeerOptions()
27{
ed5f5120
SM
28 // init options consistent with an empty sslOptions
29 parseOptions();
d7208dbc
AJ
30}
31
0b0e0864
AJ
32void
33Security::PeerOptions::parse(const char *token)
34{
d4ab7b63
AJ
35 if (!*token) {
36 // config says just "ssl" or "tls" (or "tls-")
37 encryptTransport = true;
38 return;
39 }
40
41ee8990
AJ
41 if (strncmp(token, "disable", 7) == 0) {
42 clear();
d4ab7b63
AJ
43 return;
44 }
45
46 if (strncmp(token, "cert=", 5) == 0) {
d1d72d43
AJ
47 KeyData t;
48 t.privateKeyFile = t.certFile = SBuf(token + 5);
49 certs.emplace_back(t);
0b0e0864 50 } else if (strncmp(token, "key=", 4) == 0) {
d1d72d43 51 if (certs.empty() || certs.back().certFile.isEmpty()) {
28e1d824 52 fatal("cert= option must be set before key= is used.");
d1d72d43 53 return;
0b0e0864 54 }
d1d72d43
AJ
55 KeyData &t = certs.back();
56 t.privateKeyFile = SBuf(token + 4);
0b0e0864 57 } else if (strncmp(token, "version=", 8) == 0) {
2aa5a7d3 58 debugs(0, DBG_PARSE_NOTE(1), "UPGRADE WARNING: SSL version= is deprecated. Use options= and tls-min-version= to limit protocols instead.");
0b0e0864 59 sslVersion = xatoi(token + 8);
1cc44095
AJ
60 } else if (strncmp(token, "min-version=", 12) == 0) {
61 tlsMinVersion = SBuf(token + 12);
5dc9fd64 62 optsReparse = true;
0b0e0864
AJ
63 } else if (strncmp(token, "options=", 8) == 0) {
64 sslOptions = SBuf(token + 8);
2aa5a7d3 65 optsReparse = true;
0b0e0864
AJ
66 } else if (strncmp(token, "cipher=", 7) == 0) {
67 sslCipher = SBuf(token + 7);
68 } else if (strncmp(token, "cafile=", 7) == 0) {
86a84cc0 69 caFiles.emplace_back(SBuf(token + 7));
0b0e0864
AJ
70 } else if (strncmp(token, "capath=", 7) == 0) {
71 caDir = SBuf(token + 7);
86a84cc0
AJ
72#if !USE_OPENSSL
73 debugs(3, DBG_PARSE_NOTE(1), "WARNING: capath= option requires --with-openssl.");
74#endif
0b0e0864
AJ
75 } else if (strncmp(token, "crlfile=", 8) == 0) {
76 crlFile = SBuf(token + 8);
6b19d1f9 77 loadCrlFile();
0b0e0864 78 } else if (strncmp(token, "flags=", 6) == 0) {
b24e9ae7 79 if (parsedFlags != 0) {
9a622f3e 80 debugs(3, DBG_PARSE_NOTE(1), "WARNING: Overwriting flags=" << sslFlags << " with " << SBuf(token + 6));
b24e9ae7 81 }
0b0e0864 82 sslFlags = SBuf(token + 6);
ec4defdb 83 parsedFlags = parseFlags();
435c72b0
AJ
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);
0b0e0864
AJ
92 } else if (strncmp(token, "domain=", 7) == 0) {
93 sslDomain = SBuf(token + 7);
b05d749d
AJ
94 } else if (strncmp(token, "no-npn", 6) == 0) {
95 flags.tlsNpn = false;
9a622f3e
AJ
96 } else {
97 debugs(3, DBG_CRITICAL, "ERROR: Unknown TLS option '" << token << "'");
d4ab7b63 98 return;
0b0e0864 99 }
d4ab7b63
AJ
100
101 encryptTransport = true;
0b0e0864
AJ
102}
103
8250ca31
AJ
104void
105Security::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
d1d72d43
AJ
112 for (auto &i : certs) {
113 if (!i.certFile.isEmpty())
114 p->appendf(" %scert=" SQUIDSBUFPH, pfx, SQUIDSBUFPRINT(i.certFile));
8250ca31 115
d1d72d43
AJ
116 if (!i.privateKeyFile.isEmpty() && i.privateKeyFile != i.certFile)
117 p->appendf(" %skey=" SQUIDSBUFPH, pfx, SQUIDSBUFPRINT(i.privateKeyFile));
118 }
8250ca31
AJ
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
86a84cc0
AJ
126 for (auto i : caFiles) {
127 p->appendf(" %scafile=" SQUIDSBUFPH, pfx, SQUIDSBUFPRINT(i));
128 }
8250ca31
AJ
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));
0278bdcb 138
435c72b0
AJ
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 }
b05d749d
AJ
147
148 if (!flags.tlsNpn)
149 p->appendf(" %sno-npn", pfx);
8250ca31
AJ
150}
151
585c27eb
AJ
152void
153Security::PeerOptions::updateTlsVersionLimits()
9a2f63e7 154{
1cc44095
AJ
155 if (!tlsMinVersion.isEmpty()) {
156 ::Parser::Tokenizer tok(tlsMinVersion);
157 int64_t v = 0;
5dc9fd64 158 tlsMinOptions.clear();
bd71ba99 159 if (tok.skip('1') && tok.skip('.') && tok.int64(v, 10, false, 1) && v <= 3) {
1cc44095 160 // only account for TLS here - SSL versions are handled by options= parameter
bd71ba99 161 // avoid affecting options= parameter in cachemgr config report
5dc9fd64 162 SBuf add;
cc488ec9 163#if USE_OPENSSL
1cc44095 164 if (v > 0)
5dc9fd64 165 add.append(":NO_TLSv1");
1cc44095 166 if (v > 1)
5dc9fd64 167 add.append(":NO_TLSv1_1");
1cc44095 168 if (v > 2)
5dc9fd64 169 add.append(":NO_TLSv1_2");
cc488ec9 170#elif USE_GNUTLS
cc488ec9
AJ
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");
5dc9fd64 177#endif
cc488ec9 178
5dc9fd64 179 if (!tlsMinOptions.isEmpty())
cc488ec9 180 add.chop(1); // remove the initial ':'
5dc9fd64 181 tlsMinOptions.append(add);
2aa5a7d3 182 optsReparse = true;
1cc44095
AJ
183
184 } else {
185 debugs(0, DBG_PARSE_NOTE(1), "WARNING: Unknown TLS minimum version: " << tlsMinVersion);
186 }
187
cc488ec9
AJ
188 return;
189 }
190
ed5f5120 191 if (sslVersion > 2) {
1cc44095
AJ
192 // backward compatibility hack for sslversion= configuration
193 // only use if tls-min-version=N.N is not present
8250ca31
AJ
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
5badbadf 196 const char *add = nullptr;
1cc44095
AJ
197 switch (sslVersion) {
198 case 3:
cc488ec9 199#if USE_OPENSSL
d39e23a0 200 add = ":NO_TLSv1:NO_TLSv1_1:NO_TLSv1_2:NO_TLSv1_3";
cc488ec9 201#elif USE_GNUTLS
d39e23a0 202 add = ":-VERS-TLS1.0:-VERS-TLS1.1:-VERS-TLS1.2:-VERS-TLS1.3";
cc488ec9 203#endif
1cc44095
AJ
204 break;
205 case 4:
cc488ec9 206#if USE_OPENSSL
d39e23a0 207 add = ":NO_SSLv3:NO_TLSv1_1:NO_TLSv1_2:NO_TLSv1_3";
cc488ec9 208#elif USE_GNUTLS
d39e23a0 209 add = ":+VERS-TLS1.0:-VERS-TLS1.1:-VERS-TLS1.2:-VERS-TLS1.3";
cc488ec9 210#endif
1cc44095
AJ
211 break;
212 case 5:
cc488ec9 213#if USE_OPENSSL
d39e23a0 214 add = ":NO_SSLv3:NO_TLSv1:NO_TLSv1_2:NO_TLSv1_3";
cc488ec9 215#elif USE_GNUTLS
d39e23a0 216 add = ":-VERS-TLS1.0:+VERS-TLS1.1:-VERS-TLS1.2:-VERS-TLS1.3";
cc488ec9 217#endif
1cc44095
AJ
218 break;
219 case 6:
cc488ec9 220#if USE_OPENSSL
d39e23a0 221 add = ":NO_SSLv3:NO_TLSv1:NO_TLSv1_1:NO_TLSv1_3";
cc488ec9 222#elif USE_GNUTLS
d39e23a0 223 add = ":-VERS-TLS1.0:-VERS-TLS1.1:-VERS-TLS1.3";
cc488ec9 224#endif
1cc44095
AJ
225 break;
226 default: // nothing
227 break;
228 }
229 if (add) {
cc488ec9
AJ
230 if (sslOptions.isEmpty())
231 sslOptions.append(add+1, strlen(add+1));
232 else
233 sslOptions.append(add, strlen(add));
2aa5a7d3 234 optsReparse = true;
1cc44095
AJ
235 }
236 sslVersion = 0; // prevent sslOptions being repeatedly appended
237 }
585c27eb 238}
1cc44095 239
64769c79 240Security::ContextPointer
885f0ecf
AJ
241Security::PeerOptions::createBlankContext() const
242{
64769c79 243 Security::ContextPointer ctx;
885f0ecf 244#if USE_OPENSSL
0a28c16a
AJ
245 Ssl::Initialize();
246
64769c79 247 SSL_CTX *t = SSL_CTX_new(TLS_client_method());
885f0ecf 248 if (!t) {
ea574635
AJ
249 const auto x = ERR_get_error();
250 fatalf("Failed to allocate TLS client context: %s\n", Security::ErrorString(x));
885f0ecf 251 }
df473b36 252 ctx = convertContextFromRawPtr(t);
885f0ecf
AJ
253
254#elif USE_GNUTLS
255 // Initialize for X.509 certificate exchange
64769c79 256 gnutls_certificate_credentials_t t;
83b053a0 257 if (const auto x = gnutls_certificate_allocate_credentials(&t)) {
ea574635 258 fatalf("Failed to allocate TLS client context: %s\n", Security::ErrorString(x));
885f0ecf 259 }
df473b36 260 ctx = convertContextFromRawPtr(t);
885f0ecf
AJ
261
262#else
c75aba02 263 debugs(83, 1, "WARNING: Failed to allocate TLS client context: No TLS library");
885f0ecf
AJ
264
265#endif
266
64769c79 267 return ctx;
885f0ecf
AJ
268}
269
900daee3 270Security::ContextPointer
a465e144 271Security::PeerOptions::createClientContext(bool setOptions)
9a2f63e7 272{
585c27eb 273 updateTlsVersionLimits();
86a84cc0 274
900daee3 275 Security::ContextPointer t(createBlankContext());
c75aba02 276 if (t) {
cf487124
AJ
277 if (setOptions)
278 updateContextOptions(t);
9a2f63e7 279#if USE_OPENSSL
c75aba02 280 // XXX: temporary performance regression. c_str() data copies and prevents this being a const method
cc488ec9 281 Ssl::InitClientContext(t, *this, parsedFlags);
9a2f63e7 282#endif
b05d749d 283 updateContextNpn(t);
b23f5f9c
AJ
284 updateContextCa(t);
285 updateContextCrl(t);
98f951b7 286 updateContextTrust(t);
86a84cc0 287 }
6b19d1f9 288
900daee3 289 return t;
9a2f63e7 290}
1f1f29e8 291
cc488ec9 292#if USE_OPENSSL
6bd62757
AJ
293/// set of options we can parse and what they map to
294static 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 },
5dc9fd64
AJ
389#else
390 { "NO_TLSv1", 0 },
6bd62757
AJ
391#endif
392#if SSL_OP_NO_TLSv1_1
393 {
394 "NO_TLSv1_1", SSL_OP_NO_TLSv1_1
395 },
5dc9fd64
AJ
396#else
397 { "NO_TLSv1_1", 0 },
6bd62757
AJ
398#endif
399#if SSL_OP_NO_TLSv1_2
400 {
401 "NO_TLSv1_2", SSL_OP_NO_TLSv1_2
402 },
5dc9fd64
AJ
403#else
404 { "NO_TLSv1_2", 0 },
6bd62757 405#endif
d39e23a0
CT
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
6bd62757
AJ
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 },
585c27eb
AJ
422#endif
423#if SSL_OP_SINGLE_ECDH_USE
424 {
425 "SINGLE_ECDH_USE", SSL_OP_SINGLE_ECDH_USE
426 },
6bd62757
AJ
427#endif
428 {
429 "", 0
430 },
431 {
432 NULL, 0
433 }
434};
cc488ec9 435#endif /* USE_OPENSSL */
6bd62757 436
c62717bd
AJ
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 */
cc488ec9 441void
5badbadf 442Security::PeerOptions::parseOptions()
6bd62757 443{
2aa5a7d3
AJ
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
5dc9fd64
AJ
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
cc488ec9 456#if USE_OPENSSL
5dc9fd64 457 ::Parser::Tokenizer tok(str);
7ae61630 458 long op = 0;
6bd62757 459
d7208dbc 460 while (!tok.atEnd()) {
6bd62757
AJ
461 enum {
462 MODE_ADD, MODE_REMOVE
463 } mode;
464
c62717bd 465 if (tok.skip('-') || tok.skip('!'))
6bd62757 466 mode = MODE_REMOVE;
c62717bd
AJ
467 else {
468 (void)tok.skip('+'); // default action is add. ignore if missing operator
6bd62757 469 mode = MODE_ADD;
c62717bd 470 }
6bd62757 471
c62717bd
AJ
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
144acb47
AJ
477 // Bug 4429: identify the full option name before determining text or numeric
478 if (tok.prefix(option, optChars)) {
6bd62757 479
c62717bd
AJ
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 }
6bd62757 486 }
144acb47
AJ
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 }
6bd62757
AJ
493 }
494
40bc593b
AJ
495 if (value) {
496 switch (mode) {
497 case MODE_ADD:
498 op |= value;
499 break;
daf640c8 500 case MODE_REMOVE:
40bc593b
AJ
501 op &= ~value;
502 break;
503 }
504 } else {
505 debugs(83, DBG_PARSE_NOTE(1), "ERROR: Unknown TLS option " << option);
6bd62757
AJ
506 }
507
c62717bd
AJ
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 }
6bd62757 512
d7208dbc 513 }
6bd62757
AJ
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
5badbadf 519 parsedOptions = op;
cc488ec9
AJ
520
521#elif USE_GNUTLS
5dc9fd64 522 if (str.isEmpty()) {
5badbadf
AJ
523 parsedOptions.reset();
524 return;
525 }
526
cc488ec9 527 const char *err = nullptr;
5dc9fd64 528 const char *priorities = str.c_str();
cc488ec9 529 gnutls_priority_t op;
83b053a0 530 const auto x = gnutls_priority_init(&op, priorities, &err);
60fcfadf
AJ
531 if (x != GNUTLS_E_SUCCESS) {
532 fatalf("(%s) in TLS options '%s'", ErrorString(x), err);
cc488ec9 533 }
c17dcc9a 534 parsedOptions = Security::ParsedOptions(op, [](gnutls_priority_t p) {
df473b36 535 debugs(83, 5, "gnutls_priority_deinit p=" << (void*)p);
ed5f5120 536 gnutls_priority_deinit(p);
c17dcc9a 537 });
cc488ec9 538#endif
6bd62757
AJ
539}
540
ec4defdb
AJ
541/**
542 * Parses the TLS flags squid.conf parameter
543 */
983fab6e 544Security::ParsedPortFlags
ec4defdb 545Security::PeerOptions::parseFlags()
b24e9ae7 546{
ec4defdb 547 if (sslFlags.isEmpty())
b24e9ae7
AJ
548 return 0;
549
550 static struct {
551 SBuf label;
983fab6e 552 ParsedPortFlags mask;
b24e9ae7
AJ
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 },
983fab6e 557 { SBuf("CONDITIONAL_AUTH"), SSL_FLAG_CONDITIONAL_AUTH },
b24e9ae7
AJ
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
ec4defdb 567 ::Parser::Tokenizer tok(sslFlags);
b24e9ae7
AJ
568 static const CharacterSet delims("Flag-delimiter", ":,");
569
983fab6e 570 ParsedPortFlags fl = 0;
b24e9ae7 571 do {
983fab6e 572 ParsedPortFlags found = 0;
b24e9ae7 573 for (size_t i = 0; flagTokens[i].mask; ++i) {
983fab6e 574 // XXX: skips FOO in FOOBAR, missing merged flags and trailing typos
3f6c1586 575 if (tok.skip(flagTokens[i].label)) {
b24e9ae7
AJ
576 found = flagTokens[i].mask;
577 break;
578 }
579 }
580 if (!found)
8250ca31 581 fatalf("Unknown TLS flag '" SQUIDSBUFPH "'", SQUIDSBUFPRINT(tok.remaining()));
8b253b83 582 if (found == SSL_FLAG_NO_DEFAULT_CA) {
435c72b0
AJ
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);
8b253b83
AJ
587 } else
588 fl |= found;
b24e9ae7
AJ
589 } while (tok.skipOne(delims));
590
983fab6e 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
b24e9ae7
AJ
603 return fl;
604}
605
6b19d1f9
AJ
606/// Load a CRLs list stored in the file whose /path/name is in crlFile
607/// replaces any CRL loaded previously
608void
609Security::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
cf487124 629void
2aa5a7d3 630Security::PeerOptions::updateContextOptions(Security::ContextPointer &ctx)
cf487124 631{
2aa5a7d3 632 parseOptions();
cf487124
AJ
633#if USE_OPENSSL
634 SSL_CTX_set_options(ctx.get(), parsedOptions);
635#elif USE_GNUTLS
b491f761 636 // NP: GnuTLS uses 'priorities' which are set only per-session instead.
cf487124
AJ
637#endif
638}
639
b05d749d
AJ
640#if USE_OPENSSL && defined(TLSEXT_TYPE_next_proto_neg)
641// Dummy next_proto_neg callback
642static int
643ssl_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
651void
64769c79 652Security::PeerOptions::updateContextNpn(Security::ContextPointer &ctx)
b05d749d
AJ
653{
654 if (!flags.tlsNpn)
655 return;
656
657#if USE_OPENSSL && defined(TLSEXT_TYPE_next_proto_neg)
64769c79 658 SSL_CTX_set_next_proto_select_cb(ctx.get(), &ssl_next_proto_cb, nullptr);
b05d749d
AJ
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
4f4d3a57 665static const char *
b23f5f9c 666loadSystemTrustedCa(Security::ContextPointer &ctx)
4f4d3a57 667{
dfe70a1b 668 debugs(83, 8, "Setting default system Trusted CA. ctx=" << (void*)ctx.get());
4f4d3a57 669#if USE_OPENSSL
b23f5f9c 670 if (SSL_CTX_set_default_verify_paths(ctx.get()) == 0)
ea574635 671 return Security::ErrorString(ERR_get_error());
4f4d3a57
AJ
672
673#elif USE_GNUTLS
b23f5f9c 674 auto x = gnutls_certificate_set_x509_system_trust(ctx.get());
4f4d3a57 675 if (x < 0)
ea574635 676 return Security::ErrorString(x);
4f4d3a57
AJ
677
678#endif
679 return nullptr;
680}
681
86a84cc0 682void
b23f5f9c 683Security::PeerOptions::updateContextCa(Security::ContextPointer &ctx)
86a84cc0
AJ
684{
685 debugs(83, 8, "Setting CA certificate locations.");
123d5c2a 686#if USE_OPENSSL
c8736993
CT
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 }
123d5c2a 693#endif
86a84cc0
AJ
694 for (auto i : caFiles) {
695#if USE_OPENSSL
c8736993 696 if (!SSL_CTX_load_verify_locations(ctx.get(), i.c_str(), nullptr)) {
ea574635
AJ
697 const auto x = ERR_get_error();
698 debugs(83, DBG_IMPORTANT, "WARNING: Ignoring error setting CA certificate location " <<
699 i << ": " << Security::ErrorString(x));
86a84cc0
AJ
700 }
701#elif USE_GNUTLS
ea574635
AJ
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));
86a84cc0
AJ
706 }
707#endif
708 }
709
b2cd014b 710 if (!flags.tlsDefaultCa)
86a84cc0
AJ
711 return;
712
4f4d3a57
AJ
713 if (const char *err = loadSystemTrustedCa(ctx)) {
714 debugs(83, DBG_IMPORTANT, "WARNING: Ignoring error setting default trusted CA : " << err);
86a84cc0 715 }
86a84cc0
AJ
716}
717
6b19d1f9 718void
b23f5f9c 719Security::PeerOptions::updateContextCrl(Security::ContextPointer &ctx)
6b19d1f9
AJ
720{
721#if USE_OPENSSL
722 bool verifyCrl = false;
b23f5f9c 723 X509_STORE *st = SSL_CTX_get_cert_store(ctx.get());
6b19d1f9
AJ
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
98f951b7
AR
743void
744Security::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
cc488ec9
AJ
760void
761Security::PeerOptions::updateSessionOptions(Security::SessionPointer &s)
762{
2aa5a7d3 763 parseOptions();
c96b5508 764#if USE_OPENSSL
60fcfadf 765 debugs(83, 5, "set OpenSSL options for session=" << s << ", parsedOptions=" << parsedOptions);
b491f761
AJ
766 // XXX: Options already set before (via the context) are not cleared!
767 SSL_set_options(s.get(), parsedOptions);
768
c96b5508 769#elif USE_GNUTLS
83b053a0 770 LibErrorCode x;
5badbadf 771 SBuf errMsg;
cc488ec9
AJ
772 if (!parsedOptions) {
773 debugs(83, 5, "set GnuTLS default priority/options for session=" << s);
774 x = gnutls_set_default_priority(s.get());
5badbadf
AJ
775 static const SBuf defaults("default");
776 errMsg = defaults;
cc488ec9 777 } else {
60fcfadf 778 debugs(83, 5, "set GnuTLS session=" << s << ", options='" << sslOptions << ":" << tlsMinOptions << "'");
cc488ec9 779 x = gnutls_priority_set(s.get(), parsedOptions.get());
5badbadf 780 errMsg = sslOptions;
cc488ec9
AJ
781 }
782
783 if (x != GNUTLS_E_SUCCESS) {
60fcfadf 784 debugs(83, DBG_IMPORTANT, "ERROR: session=" << s << " Failed to set TLS options (" << errMsg << ":" << tlsMinVersion << "). error: " << Security::ErrorString(x));
cc488ec9
AJ
785 }
786#endif
787}
788
1f1f29e8
AJ
789void
790parse_securePeerOptions(Security::PeerOptions *opt)
791{
792 while(const char *token = ConfigParser::NextToken())
793 opt->parse(token);
151644b7 794 opt->parseOptions();
1f1f29e8
AJ
795}
796