2 * Copyright (C) 1996-2017 The Squid Software Foundation and contributors
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.
10 #include "base/Packable.h"
14 #include "parser/Tokenizer.h"
16 #include "security/PeerOptions.h"
19 #include "ssl/support.h"
22 Security::PeerOptions
Security::ProxyOutgoingConfig
;
24 Security::PeerOptions::PeerOptions(const Security::PeerOptions
&p
) :
25 sslOptions(p
.sslOptions
),
28 sslCipher(p
.sslCipher
),
30 sslDomain(p
.sslDomain
),
31 parsedOptions(p
.parsedOptions
),
32 parsedFlags(p
.parsedFlags
),
35 parsedCrl(p
.parsedCrl
),
36 sslVersion(p
.sslVersion
),
37 encryptTransport(p
.encryptTransport
)
39 memcpy(&flags
, &p
.flags
, sizeof(flags
));
43 Security::PeerOptions::parse(const char *token
)
46 // config says just "ssl" or "tls" (or "tls-")
47 encryptTransport
= true;
51 if (strncmp(token
, "disable", 7) == 0) {
56 if (strncmp(token
, "cert=", 5) == 0) {
58 t
.privateKeyFile
= t
.certFile
= SBuf(token
+ 5);
59 certs
.emplace_back(t
);
60 } else if (strncmp(token
, "key=", 4) == 0) {
61 if (certs
.empty() || certs
.back().certFile
.isEmpty()) {
62 fatal("cert= option must be set before key= is used.");
65 KeyData
&t
= certs
.back();
66 t
.privateKeyFile
= SBuf(token
+ 4);
67 } else if (strncmp(token
, "version=", 8) == 0) {
68 debugs(0, DBG_PARSE_NOTE(1), "UPGRADE WARNING: SSL version= is deprecated. Use options= to limit protocols instead.");
69 sslVersion
= xatoi(token
+ 8);
70 } else if (strncmp(token
, "min-version=", 12) == 0) {
71 tlsMinVersion
= SBuf(token
+ 12);
72 } else if (strncmp(token
, "options=", 8) == 0) {
73 sslOptions
= SBuf(token
+ 8);
74 parsedOptions
= parseOptions();
75 } else if (strncmp(token
, "cipher=", 7) == 0) {
76 sslCipher
= SBuf(token
+ 7);
77 } else if (strncmp(token
, "cafile=", 7) == 0) {
78 caFiles
.emplace_back(SBuf(token
+ 7));
79 } else if (strncmp(token
, "capath=", 7) == 0) {
80 caDir
= SBuf(token
+ 7);
82 debugs(3, DBG_PARSE_NOTE(1), "WARNING: capath= option requires --with-openssl.");
84 } else if (strncmp(token
, "crlfile=", 8) == 0) {
85 crlFile
= SBuf(token
+ 8);
87 } else if (strncmp(token
, "flags=", 6) == 0) {
88 if (parsedFlags
!= 0) {
89 debugs(3, DBG_PARSE_NOTE(1), "WARNING: Overwriting flags=" << sslFlags
<< " with " << SBuf(token
+ 6));
91 sslFlags
= SBuf(token
+ 6);
92 parsedFlags
= parseFlags();
93 } else if (strncmp(token
, "default-ca=off", 14) == 0 || strncmp(token
, "no-default-ca", 13) == 0) {
94 if (flags
.tlsDefaultCa
.configured() && flags
.tlsDefaultCa
)
95 fatalf("ERROR: previous default-ca settings conflict with %s", token
);
96 flags
.tlsDefaultCa
.configure(false);
97 } else if (strncmp(token
, "default-ca=on", 13) == 0 || strncmp(token
, "default-ca", 10) == 0) {
98 if (flags
.tlsDefaultCa
.configured() && !flags
.tlsDefaultCa
)
99 fatalf("ERROR: previous default-ca settings conflict with %s", token
);
100 flags
.tlsDefaultCa
.configure(true);
101 } else if (strncmp(token
, "domain=", 7) == 0) {
102 sslDomain
= SBuf(token
+ 7);
103 } else if (strncmp(token
, "no-npn", 6) == 0) {
104 flags
.tlsNpn
= false;
106 debugs(3, DBG_CRITICAL
, "ERROR: Unknown TLS option '" << token
<< "'");
110 encryptTransport
= true;
114 Security::PeerOptions::dumpCfg(Packable
*p
, const char *pfx
) const
116 if (!encryptTransport
) {
117 p
->appendf(" %sdisable", pfx
);
118 return; // no other settings are relevant
121 for (auto &i
: certs
) {
122 if (!i
.certFile
.isEmpty())
123 p
->appendf(" %scert=" SQUIDSBUFPH
, pfx
, SQUIDSBUFPRINT(i
.certFile
));
125 if (!i
.privateKeyFile
.isEmpty() && i
.privateKeyFile
!= i
.certFile
)
126 p
->appendf(" %skey=" SQUIDSBUFPH
, pfx
, SQUIDSBUFPRINT(i
.privateKeyFile
));
129 if (!sslOptions
.isEmpty())
130 p
->appendf(" %soptions=" SQUIDSBUFPH
, pfx
, SQUIDSBUFPRINT(sslOptions
));
132 if (!sslCipher
.isEmpty())
133 p
->appendf(" %scipher=" SQUIDSBUFPH
, pfx
, SQUIDSBUFPRINT(sslCipher
));
135 for (auto i
: caFiles
) {
136 p
->appendf(" %scafile=" SQUIDSBUFPH
, pfx
, SQUIDSBUFPRINT(i
));
139 if (!caDir
.isEmpty())
140 p
->appendf(" %scapath=" SQUIDSBUFPH
, pfx
, SQUIDSBUFPRINT(caDir
));
142 if (!crlFile
.isEmpty())
143 p
->appendf(" %scrlfile=" SQUIDSBUFPH
, pfx
, SQUIDSBUFPRINT(crlFile
));
145 if (!sslFlags
.isEmpty())
146 p
->appendf(" %sflags=" SQUIDSBUFPH
, pfx
, SQUIDSBUFPRINT(sslFlags
));
148 if (flags
.tlsDefaultCa
.configured()) {
149 // default ON for peers / upstream servers
150 // default OFF for listening ports
151 if (flags
.tlsDefaultCa
)
152 p
->appendf(" %sdefault-ca", pfx
);
154 p
->appendf(" %sdefault-ca=off", pfx
);
158 p
->appendf(" %sno-npn", pfx
);
162 Security::PeerOptions::updateTlsVersionLimits()
164 if (!tlsMinVersion
.isEmpty()) {
165 ::Parser::Tokenizer
tok(tlsMinVersion
);
167 if (tok
.skip('1') && tok
.skip('.') && tok
.int64(v
, 10, false, 1) && v
<= 3) {
168 // only account for TLS here - SSL versions are handled by options= parameter
169 // avoid affecting options= parameter in cachemgr config report
172 parsedOptions
|= SSL_OP_NO_TLSv1
;
174 #if SSL_OP_NO_TLSv1_1
176 parsedOptions
|= SSL_OP_NO_TLSv1_1
;
178 #if SSL_OP_NO_TLSv1_2
180 parsedOptions
|= SSL_OP_NO_TLSv1_2
;
184 debugs(0, DBG_PARSE_NOTE(1), "WARNING: Unknown TLS minimum version: " << tlsMinVersion
);
187 } else if (sslVersion
> 2) {
188 // backward compatibility hack for sslversion= configuration
189 // only use if tls-min-version=N.N is not present
190 // values 0-2 for auto and SSLv2 are not supported any longer.
191 // Do it this way so we DO cause changes to options= in cachemgr config report
192 const char *add
= NULL
;
193 switch (sslVersion
) {
195 add
= "NO_TLSv1,NO_TLSv1_1,NO_TLSv1_2";
198 add
= "NO_SSLv3,NO_TLSv1_1,NO_TLSv1_2";
201 add
= "NO_SSLv3,NO_TLSv1,NO_TLSv1_2";
204 add
= "NO_SSLv3,NO_TLSv1,NO_TLSv1_1";
210 if (!sslOptions
.isEmpty())
211 sslOptions
.append(",",1);
212 sslOptions
.append(add
, strlen(add
));
214 sslVersion
= 0; // prevent sslOptions being repeatedly appended
218 Security::ContextPointer
219 Security::PeerOptions::createBlankContext() const
221 Security::ContextPointer ctx
;
225 #if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
226 SSL_CTX
*t
= SSL_CTX_new(TLS_client_method());
228 SSL_CTX
*t
= SSL_CTX_new(SSLv23_client_method());
231 const auto x
= ERR_get_error();
232 fatalf("Failed to allocate TLS client context: %s\n", Security::ErrorString(x
));
234 ctx
.resetWithoutLocking(t
);
237 // Initialize for X.509 certificate exchange
238 gnutls_certificate_credentials_t t
;
239 if (const int x
= gnutls_certificate_allocate_credentials(&t
)) {
240 fatalf("Failed to allocate TLS client context: %s\n", Security::ErrorString(x
));
242 ctx
.resetWithoutLocking(t
);
245 debugs(83, 1, "WARNING: Failed to allocate TLS client context: No TLS library");
252 Security::ContextPointer
253 Security::PeerOptions::createClientContext(bool setOptions
)
255 updateTlsVersionLimits();
257 Security::ContextPointer
t(createBlankContext());
260 // XXX: temporary performance regression. c_str() data copies and prevents this being a const method
261 Ssl::InitClientContext(t
, *this, (setOptions
? parsedOptions
: 0), parsedFlags
);
271 /// set of options we can parse and what they map to
272 static struct ssl_option
{
278 #if SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG
280 "NETSCAPE_REUSE_CIPHER_CHANGE_BUG", SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG
283 #if SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG
285 "SSLREF2_REUSE_CERT_TYPE_BUG", SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG
288 #if SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER
290 "MICROSOFT_BIG_SSLV3_BUFFER", SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER
293 #if SSL_OP_SSLEAY_080_CLIENT_DH_BUG
295 "SSLEAY_080_CLIENT_DH_BUG", SSL_OP_SSLEAY_080_CLIENT_DH_BUG
298 #if SSL_OP_TLS_D5_BUG
300 "TLS_D5_BUG", SSL_OP_TLS_D5_BUG
303 #if SSL_OP_TLS_BLOCK_PADDING_BUG
305 "TLS_BLOCK_PADDING_BUG", SSL_OP_TLS_BLOCK_PADDING_BUG
308 #if SSL_OP_TLS_ROLLBACK_BUG
310 "TLS_ROLLBACK_BUG", SSL_OP_TLS_ROLLBACK_BUG
315 "ALL", (long)SSL_OP_ALL
318 #if SSL_OP_SINGLE_DH_USE
320 "SINGLE_DH_USE", SSL_OP_SINGLE_DH_USE
323 #if SSL_OP_EPHEMERAL_RSA
325 "EPHEMERAL_RSA", SSL_OP_EPHEMERAL_RSA
328 #if SSL_OP_PKCS1_CHECK_1
330 "PKCS1_CHECK_1", SSL_OP_PKCS1_CHECK_1
333 #if SSL_OP_PKCS1_CHECK_2
335 "PKCS1_CHECK_2", SSL_OP_PKCS1_CHECK_2
338 #if SSL_OP_NETSCAPE_CA_DN_BUG
340 "NETSCAPE_CA_DN_BUG", SSL_OP_NETSCAPE_CA_DN_BUG
343 #if SSL_OP_NON_EXPORT_FIRST
345 "NON_EXPORT_FIRST", SSL_OP_NON_EXPORT_FIRST
348 #if SSL_OP_CIPHER_SERVER_PREFERENCE
350 "CIPHER_SERVER_PREFERENCE", SSL_OP_CIPHER_SERVER_PREFERENCE
353 #if SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG
355 "NETSCAPE_DEMO_CIPHER_CHANGE_BUG", SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG
360 "NO_SSLv3", SSL_OP_NO_SSLv3
365 "NO_TLSv1", SSL_OP_NO_TLSv1
368 #if SSL_OP_NO_TLSv1_1
370 "NO_TLSv1_1", SSL_OP_NO_TLSv1_1
373 #if SSL_OP_NO_TLSv1_2
375 "NO_TLSv1_2", SSL_OP_NO_TLSv1_2
378 #if SSL_OP_NO_COMPRESSION
380 "No_Compression", SSL_OP_NO_COMPRESSION
385 "NO_TICKET", SSL_OP_NO_TICKET
388 #if SSL_OP_SINGLE_ECDH_USE
390 "SINGLE_ECDH_USE", SSL_OP_SINGLE_ECDH_USE
402 * Pre-parse TLS options= parameter to be applied when the TLS objects created.
403 * Options must not used in the case of peek or stare bump mode.
406 Security::PeerOptions::parseOptions()
409 ::Parser::Tokenizer
tok(sslOptions
);
413 MODE_ADD
, MODE_REMOVE
416 if (tok
.skip('-') || tok
.skip('!'))
419 (void)tok
.skip('+'); // default action is add. ignore if missing operator
423 static const CharacterSet optChars
= CharacterSet("TLS-option", "_") + CharacterSet::ALPHA
+ CharacterSet::DIGIT
;
428 // Bug 4429: identify the full option name before determining text or numeric
429 if (tok
.prefix(option
, optChars
)) {
431 // find the named option in our supported set
432 for (struct ssl_option
*opttmp
= ssl_options
; opttmp
->name
; ++opttmp
) {
433 if (option
.cmp(opttmp
->name
) == 0) {
434 value
= opttmp
->value
;
439 // Special case.. hex specification
440 ::Parser::Tokenizer
tmp(option
);
441 if (!value
&& tmp
.int64(hex
, 16, false) && tmp
.atEnd()) {
456 debugs(83, DBG_PARSE_NOTE(1), "ERROR: Unknown TLS option " << option
);
459 static const CharacterSet
delims("TLS-option-delim",":,");
460 if (!tok
.skipAll(delims
) && !tok
.atEnd()) {
461 fatalf("Unknown TLS option '" SQUIDSBUFPH
"'", SQUIDSBUFPRINT(tok
.remaining()));
464 } while (!tok
.atEnd());
467 // compliance with RFC 6176: Prohibiting Secure Sockets Layer (SSL) Version 2.0
468 op
= op
| SSL_OP_NO_SSLv2
;
474 * Parses the TLS flags squid.conf parameter
477 Security::PeerOptions::parseFlags()
479 if (sslFlags
.isEmpty())
486 { SBuf("NO_DEFAULT_CA"), SSL_FLAG_NO_DEFAULT_CA
},
487 { SBuf("DELAYED_AUTH"), SSL_FLAG_DELAYED_AUTH
},
488 { SBuf("DONT_VERIFY_PEER"), SSL_FLAG_DONT_VERIFY_PEER
},
489 { SBuf("DONT_VERIFY_DOMAIN"), SSL_FLAG_DONT_VERIFY_DOMAIN
},
490 { SBuf("NO_SESSION_REUSE"), SSL_FLAG_NO_SESSION_REUSE
},
491 #if X509_V_FLAG_CRL_CHECK
492 { SBuf("VERIFY_CRL"), SSL_FLAG_VERIFY_CRL
},
493 { SBuf("VERIFY_CRL_ALL"), SSL_FLAG_VERIFY_CRL_ALL
},
498 ::Parser::Tokenizer
tok(sslFlags
);
499 static const CharacterSet
delims("Flag-delimiter", ":,");
504 for (size_t i
= 0; flagTokens
[i
].mask
; ++i
) {
505 if (tok
.skip(flagTokens
[i
].label
)) {
506 found
= flagTokens
[i
].mask
;
511 fatalf("Unknown TLS flag '" SQUIDSBUFPH
"'", SQUIDSBUFPRINT(tok
.remaining()));
512 if (found
== SSL_FLAG_NO_DEFAULT_CA
) {
513 if (flags
.tlsDefaultCa
.configured() && flags
.tlsDefaultCa
)
514 fatal("ERROR: previous default-ca settings conflict with sslflags=NO_DEFAULT_CA");
515 debugs(83, DBG_PARSE_NOTE(2), "WARNING: flags=NO_DEFAULT_CA is deprecated. Use tls-default-ca=off instead.");
516 flags
.tlsDefaultCa
.configure(false);
519 } while (tok
.skipOne(delims
));
524 /// Load a CRLs list stored in the file whose /path/name is in crlFile
525 /// replaces any CRL loaded previously
527 Security::PeerOptions::loadCrlFile()
530 if (crlFile
.isEmpty())
534 BIO
*in
= BIO_new_file(crlFile
.c_str(), "r");
536 debugs(83, 2, "WARNING: Failed to open CRL file " << crlFile
);
540 while (X509_CRL
*crl
= PEM_read_bio_X509_CRL(in
,NULL
,NULL
,NULL
)) {
541 parsedCrl
.emplace_back(Security::CrlPointer(crl
));
547 #if USE_OPENSSL && defined(TLSEXT_TYPE_next_proto_neg)
548 // Dummy next_proto_neg callback
550 ssl_next_proto_cb(SSL
*s
, unsigned char **out
, unsigned char *outlen
, const unsigned char *in
, unsigned int inlen
, void *arg
)
552 static const unsigned char supported_protos
[] = {8, 'h','t','t', 'p', '/', '1', '.', '1'};
553 (void)SSL_select_next_proto(out
, outlen
, in
, inlen
, supported_protos
, sizeof(supported_protos
));
554 return SSL_TLSEXT_ERR_OK
;
559 Security::PeerOptions::updateContextNpn(Security::ContextPointer
&ctx
)
564 #if USE_OPENSSL && defined(TLSEXT_TYPE_next_proto_neg)
565 SSL_CTX_set_next_proto_select_cb(ctx
.get(), &ssl_next_proto_cb
, nullptr);
568 // NOTE: GnuTLS does not support the obsolete NPN extension.
569 // it does support ALPN per-session, not per-context.
573 loadSystemTrustedCa(Security::ContextPointer
&ctx
)
576 if (SSL_CTX_set_default_verify_paths(ctx
.get()) == 0)
577 return Security::ErrorString(ERR_get_error());
580 auto x
= gnutls_certificate_set_x509_system_trust(ctx
.get());
582 return Security::ErrorString(x
);
589 Security::PeerOptions::updateContextCa(Security::ContextPointer
&ctx
)
591 debugs(83, 8, "Setting CA certificate locations.");
593 const char *path
= caDir
.isEmpty() ? nullptr : caDir
.c_str();
595 for (auto i
: caFiles
) {
597 if (!SSL_CTX_load_verify_locations(ctx
.get(), i
.c_str(), path
)) {
598 const auto x
= ERR_get_error();
599 debugs(83, DBG_IMPORTANT
, "WARNING: Ignoring error setting CA certificate location " <<
600 i
<< ": " << Security::ErrorString(x
));
603 const auto x
= gnutls_certificate_set_x509_trust_file(ctx
.get(), i
.c_str(), GNUTLS_X509_FMT_PEM
);
605 debugs(83, DBG_IMPORTANT
, "WARNING: Ignoring error setting CA certificate location " <<
606 i
<< ": " << Security::ErrorString(x
));
611 if (!flags
.tlsDefaultCa
)
614 if (const char *err
= loadSystemTrustedCa(ctx
)) {
615 debugs(83, DBG_IMPORTANT
, "WARNING: Ignoring error setting default trusted CA : " << err
);
620 Security::PeerOptions::updateContextCrl(Security::ContextPointer
&ctx
)
623 bool verifyCrl
= false;
624 X509_STORE
*st
= SSL_CTX_get_cert_store(ctx
.get());
625 if (parsedCrl
.size()) {
626 for (auto &i
: parsedCrl
) {
627 if (!X509_STORE_add_crl(st
, i
.get()))
628 debugs(83, 2, "WARNING: Failed to add CRL");
634 #if X509_V_FLAG_CRL_CHECK
635 if ((parsedFlags
& SSL_FLAG_VERIFY_CRL_ALL
))
636 X509_STORE_set_flags(st
, X509_V_FLAG_CRL_CHECK
|X509_V_FLAG_CRL_CHECK_ALL
);
637 else if (verifyCrl
|| (parsedFlags
& SSL_FLAG_VERIFY_CRL
))
638 X509_STORE_set_flags(st
, X509_V_FLAG_CRL_CHECK
);
641 #endif /* USE_OPENSSL */
645 parse_securePeerOptions(Security::PeerOptions
*opt
)
647 while(const char *token
= ConfigParser::NextToken())