2 * Copyright (C) 1996-2016 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"
15 #include "parser/Tokenizer.h"
17 #include "security/PeerOptions.h"
20 #include "ssl/support.h"
23 Security::PeerOptions
Security::ProxyOutgoingConfig
;
25 Security::PeerOptions::PeerOptions(const Security::PeerOptions
&p
) :
26 sslOptions(p
.sslOptions
),
29 sslCipher(p
.sslCipher
),
31 sslDomain(p
.sslDomain
),
32 parsedOptions(p
.parsedOptions
),
33 parsedFlags(p
.parsedFlags
),
36 parsedCrl(p
.parsedCrl
),
37 sslVersion(p
.sslVersion
),
38 encryptTransport(p
.encryptTransport
)
40 memcpy(&flags
, &p
.flags
, sizeof(flags
));
44 Security::PeerOptions::parse(const char *token
)
47 // config says just "ssl" or "tls" (or "tls-")
48 encryptTransport
= true;
52 if (strncmp(token
, "disable", 7) == 0) {
57 if (strncmp(token
, "cert=", 5) == 0) {
59 t
.privateKeyFile
= t
.certFile
= SBuf(token
+ 5);
60 certs
.emplace_back(t
);
61 } else if (strncmp(token
, "key=", 4) == 0) {
62 if (certs
.empty() || certs
.back().certFile
.isEmpty()) {
63 debugs(3, DBG_PARSE_NOTE(1), "ERROR: cert= option must be set before key= is used.");
66 KeyData
&t
= certs
.back();
67 t
.privateKeyFile
= SBuf(token
+ 4);
68 } else if (strncmp(token
, "version=", 8) == 0) {
69 debugs(0, DBG_PARSE_NOTE(1), "UPGRADE WARNING: SSL version= is deprecated. Use options= to limit protocols instead.");
70 sslVersion
= xatoi(token
+ 8);
71 } else if (strncmp(token
, "min-version=", 12) == 0) {
72 tlsMinVersion
= SBuf(token
+ 12);
73 } else if (strncmp(token
, "options=", 8) == 0) {
74 sslOptions
= SBuf(token
+ 8);
75 parsedOptions
= parseOptions();
76 } else if (strncmp(token
, "cipher=", 7) == 0) {
77 sslCipher
= SBuf(token
+ 7);
78 } else if (strncmp(token
, "cafile=", 7) == 0) {
79 caFiles
.emplace_back(SBuf(token
+ 7));
80 } else if (strncmp(token
, "capath=", 7) == 0) {
81 caDir
= SBuf(token
+ 7);
83 debugs(3, DBG_PARSE_NOTE(1), "WARNING: capath= option requires --with-openssl.");
85 } else if (strncmp(token
, "crlfile=", 8) == 0) {
86 crlFile
= SBuf(token
+ 8);
88 } else if (strncmp(token
, "flags=", 6) == 0) {
89 if (parsedFlags
!= 0) {
90 debugs(3, DBG_PARSE_NOTE(1), "WARNING: Overwriting flags=" << sslFlags
<< " with " << SBuf(token
+ 6));
92 sslFlags
= SBuf(token
+ 6);
93 parsedFlags
= parseFlags();
94 } else if (strncmp(token
, "no-default-ca", 13) == 0) {
95 flags
.tlsDefaultCa
= false;
96 } else if (strncmp(token
, "domain=", 7) == 0) {
97 sslDomain
= SBuf(token
+ 7);
98 } else if (strncmp(token
, "no-npn", 6) == 0) {
101 debugs(3, DBG_CRITICAL
, "ERROR: Unknown TLS option '" << token
<< "'");
105 encryptTransport
= true;
109 Security::PeerOptions::dumpCfg(Packable
*p
, const char *pfx
) const
111 if (!encryptTransport
) {
112 p
->appendf(" %sdisable", pfx
);
113 return; // no other settings are relevant
116 for (auto &i
: certs
) {
117 if (!i
.certFile
.isEmpty())
118 p
->appendf(" %scert=" SQUIDSBUFPH
, pfx
, SQUIDSBUFPRINT(i
.certFile
));
120 if (!i
.privateKeyFile
.isEmpty() && i
.privateKeyFile
!= i
.certFile
)
121 p
->appendf(" %skey=" SQUIDSBUFPH
, pfx
, SQUIDSBUFPRINT(i
.privateKeyFile
));
124 if (!sslOptions
.isEmpty())
125 p
->appendf(" %soptions=" SQUIDSBUFPH
, pfx
, SQUIDSBUFPRINT(sslOptions
));
127 if (!sslCipher
.isEmpty())
128 p
->appendf(" %scipher=" SQUIDSBUFPH
, pfx
, SQUIDSBUFPRINT(sslCipher
));
130 for (auto i
: caFiles
) {
131 p
->appendf(" %scafile=" SQUIDSBUFPH
, pfx
, SQUIDSBUFPRINT(i
));
134 if (!caDir
.isEmpty())
135 p
->appendf(" %scapath=" SQUIDSBUFPH
, pfx
, SQUIDSBUFPRINT(caDir
));
137 if (!crlFile
.isEmpty())
138 p
->appendf(" %scrlfile=" SQUIDSBUFPH
, pfx
, SQUIDSBUFPRINT(crlFile
));
140 if (!sslFlags
.isEmpty())
141 p
->appendf(" %sflags=" SQUIDSBUFPH
, pfx
, SQUIDSBUFPRINT(sslFlags
));
143 if (!flags
.tlsDefaultCa
)
144 p
->appendf(" %sno-default-ca", pfx
);
147 p
->appendf(" %sno-npn", pfx
);
151 Security::PeerOptions::updateTlsVersionLimits()
153 if (!tlsMinVersion
.isEmpty()) {
154 ::Parser::Tokenizer
tok(tlsMinVersion
);
156 if (tok
.skip('1') && tok
.skip('.') && tok
.int64(v
, 10, false, 1) && v
<= 3) {
157 // only account for TLS here - SSL versions are handled by options= parameter
158 // avoid affecting options= parameter in cachemgr config report
161 parsedOptions
|= SSL_OP_NO_TLSv1
;
163 #if SSL_OP_NO_TLSv1_1
165 parsedOptions
|= SSL_OP_NO_TLSv1_1
;
167 #if SSL_OP_NO_TLSv1_2
169 parsedOptions
|= SSL_OP_NO_TLSv1_2
;
173 debugs(0, DBG_PARSE_NOTE(1), "WARNING: Unknown TLS minimum version: " << tlsMinVersion
);
176 } else if (sslVersion
> 2) {
177 // backward compatibility hack for sslversion= configuration
178 // only use if tls-min-version=N.N is not present
179 // values 0-2 for auto and SSLv2 are not supported any longer.
180 // Do it this way so we DO cause changes to options= in cachemgr config report
181 const char *add
= NULL
;
182 switch (sslVersion
) {
184 add
= "NO_TLSv1,NO_TLSv1_1,NO_TLSv1_2";
187 add
= "NO_SSLv3,NO_TLSv1_1,NO_TLSv1_2";
190 add
= "NO_SSLv3,NO_TLSv1,NO_TLSv1_2";
193 add
= "NO_SSLv3,NO_TLSv1,NO_TLSv1_1";
199 if (!sslOptions
.isEmpty())
200 sslOptions
.append(",",1);
201 sslOptions
.append(add
, strlen(add
));
203 sslVersion
= 0; // prevent sslOptions being repeatedly appended
208 Security::PeerOptions::createBlankContext() const
210 Security::ContextPtr t
= nullptr;
215 #if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
216 t
= SSL_CTX_new(TLS_client_method());
218 t
= SSL_CTX_new(SSLv23_client_method());
221 const auto x
= ERR_error_string(ERR_get_error(), nullptr);
222 fatalf("Failed to allocate TLS client context: %s\n", x
);
226 // Initialize for X.509 certificate exchange
227 if (const int x
= gnutls_certificate_allocate_credentials(&t
)) {
228 fatalf("Failed to allocate TLS client context: error=%d\n", x
);
232 fatal("Failed to allocate TLS client context: No TLS library\n");
240 Security::PeerOptions::createClientContext(bool setOptions
)
242 Security::ContextPtr t
= nullptr;
244 updateTlsVersionLimits();
247 // XXX: temporary performance regression. c_str() data copies and prevents this being a const method
248 t
= sslCreateClientContext(*this, (setOptions
? parsedOptions
: 0), parsedFlags
);
250 #elif USE_GNUTLS && WHEN_READY_FOR_GNUTLS
251 t
= createBlankContext();
264 /// set of options we can parse and what they map to
265 static struct ssl_option
{
271 #if SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG
273 "NETSCAPE_REUSE_CIPHER_CHANGE_BUG", SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG
276 #if SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG
278 "SSLREF2_REUSE_CERT_TYPE_BUG", SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG
281 #if SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER
283 "MICROSOFT_BIG_SSLV3_BUFFER", SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER
286 #if SSL_OP_SSLEAY_080_CLIENT_DH_BUG
288 "SSLEAY_080_CLIENT_DH_BUG", SSL_OP_SSLEAY_080_CLIENT_DH_BUG
291 #if SSL_OP_TLS_D5_BUG
293 "TLS_D5_BUG", SSL_OP_TLS_D5_BUG
296 #if SSL_OP_TLS_BLOCK_PADDING_BUG
298 "TLS_BLOCK_PADDING_BUG", SSL_OP_TLS_BLOCK_PADDING_BUG
301 #if SSL_OP_TLS_ROLLBACK_BUG
303 "TLS_ROLLBACK_BUG", SSL_OP_TLS_ROLLBACK_BUG
308 "ALL", (long)SSL_OP_ALL
311 #if SSL_OP_SINGLE_DH_USE
313 "SINGLE_DH_USE", SSL_OP_SINGLE_DH_USE
316 #if SSL_OP_EPHEMERAL_RSA
318 "EPHEMERAL_RSA", SSL_OP_EPHEMERAL_RSA
321 #if SSL_OP_PKCS1_CHECK_1
323 "PKCS1_CHECK_1", SSL_OP_PKCS1_CHECK_1
326 #if SSL_OP_PKCS1_CHECK_2
328 "PKCS1_CHECK_2", SSL_OP_PKCS1_CHECK_2
331 #if SSL_OP_NETSCAPE_CA_DN_BUG
333 "NETSCAPE_CA_DN_BUG", SSL_OP_NETSCAPE_CA_DN_BUG
336 #if SSL_OP_NON_EXPORT_FIRST
338 "NON_EXPORT_FIRST", SSL_OP_NON_EXPORT_FIRST
341 #if SSL_OP_CIPHER_SERVER_PREFERENCE
343 "CIPHER_SERVER_PREFERENCE", SSL_OP_CIPHER_SERVER_PREFERENCE
346 #if SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG
348 "NETSCAPE_DEMO_CIPHER_CHANGE_BUG", SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG
353 "NO_SSLv3", SSL_OP_NO_SSLv3
358 "NO_TLSv1", SSL_OP_NO_TLSv1
361 #if SSL_OP_NO_TLSv1_1
363 "NO_TLSv1_1", SSL_OP_NO_TLSv1_1
366 #if SSL_OP_NO_TLSv1_2
368 "NO_TLSv1_2", SSL_OP_NO_TLSv1_2
371 #if SSL_OP_NO_COMPRESSION
373 "No_Compression", SSL_OP_NO_COMPRESSION
378 "NO_TICKET", SSL_OP_NO_TICKET
381 #if SSL_OP_SINGLE_ECDH_USE
383 "SINGLE_ECDH_USE", SSL_OP_SINGLE_ECDH_USE
395 * Pre-parse TLS options= parameter to be applied when the TLS objects created.
396 * Options must not used in the case of peek or stare bump mode.
399 Security::PeerOptions::parseOptions()
402 ::Parser::Tokenizer
tok(sslOptions
);
406 MODE_ADD
, MODE_REMOVE
409 if (tok
.skip('-') || tok
.skip('!'))
412 (void)tok
.skip('+'); // default action is add. ignore if missing operator
416 static const CharacterSet optChars
= CharacterSet("TLS-option", "_") + CharacterSet::ALPHA
+ CharacterSet::DIGIT
;
421 if (tok
.int64(hex
, 16, false)) {
422 /* Special case.. hex specification */
426 else if (tok
.prefix(option
, optChars
)) {
427 // find the named option in our supported set
428 for (struct ssl_option
*opttmp
= ssl_options
; opttmp
->name
; ++opttmp
) {
429 if (option
.cmp(opttmp
->name
) == 0) {
430 value
= opttmp
->value
;
446 debugs(83, DBG_PARSE_NOTE(1), "ERROR: Unknown TLS option " << option
);
449 static const CharacterSet
delims("TLS-option-delim",":,");
450 if (!tok
.skipAll(delims
) && !tok
.atEnd()) {
451 fatalf("Unknown TLS option '" SQUIDSBUFPH
"'", SQUIDSBUFPRINT(tok
.remaining()));
454 } while (!tok
.atEnd());
457 // compliance with RFC 6176: Prohibiting Secure Sockets Layer (SSL) Version 2.0
458 op
= op
| SSL_OP_NO_SSLv2
;
464 * Parses the TLS flags squid.conf parameter
467 Security::PeerOptions::parseFlags()
469 if (sslFlags
.isEmpty())
476 { SBuf("NO_DEFAULT_CA"), SSL_FLAG_NO_DEFAULT_CA
},
477 { SBuf("DELAYED_AUTH"), SSL_FLAG_DELAYED_AUTH
},
478 { SBuf("DONT_VERIFY_PEER"), SSL_FLAG_DONT_VERIFY_PEER
},
479 { SBuf("DONT_VERIFY_DOMAIN"), SSL_FLAG_DONT_VERIFY_DOMAIN
},
480 { SBuf("NO_SESSION_REUSE"), SSL_FLAG_NO_SESSION_REUSE
},
481 #if X509_V_FLAG_CRL_CHECK
482 { SBuf("VERIFY_CRL"), SSL_FLAG_VERIFY_CRL
},
483 { SBuf("VERIFY_CRL_ALL"), SSL_FLAG_VERIFY_CRL_ALL
},
488 ::Parser::Tokenizer
tok(sslFlags
);
489 static const CharacterSet
delims("Flag-delimiter", ":,");
494 for (size_t i
= 0; flagTokens
[i
].mask
; ++i
) {
495 if (tok
.skip(flagTokens
[i
].label
) == 0) {
496 found
= flagTokens
[i
].mask
;
501 fatalf("Unknown TLS flag '" SQUIDSBUFPH
"'", SQUIDSBUFPRINT(tok
.remaining()));
502 if (found
== SSL_FLAG_NO_DEFAULT_CA
) {
503 debugs(83, DBG_PARSE_NOTE(2), "UPGRADE WARNING: flags=NO_DEFAULT_CA is deprecated. Use tls-no-default-ca instead.");
504 flags
.tlsDefaultCa
= false;
507 } while (tok
.skipOne(delims
));
512 /// Load a CRLs list stored in the file whose /path/name is in crlFile
513 /// replaces any CRL loaded previously
515 Security::PeerOptions::loadCrlFile()
518 if (crlFile
.isEmpty())
522 BIO
*in
= BIO_new_file(crlFile
.c_str(), "r");
524 debugs(83, 2, "WARNING: Failed to open CRL file " << crlFile
);
528 while (X509_CRL
*crl
= PEM_read_bio_X509_CRL(in
,NULL
,NULL
,NULL
)) {
529 parsedCrl
.emplace_back(Security::CrlPointer(crl
));
535 #if USE_OPENSSL && defined(TLSEXT_TYPE_next_proto_neg)
536 // Dummy next_proto_neg callback
538 ssl_next_proto_cb(SSL
*s
, unsigned char **out
, unsigned char *outlen
, const unsigned char *in
, unsigned int inlen
, void *arg
)
540 static const unsigned char supported_protos
[] = {8, 'h','t','t', 'p', '/', '1', '.', '1'};
541 (void)SSL_select_next_proto(out
, outlen
, in
, inlen
, supported_protos
, sizeof(supported_protos
));
542 return SSL_TLSEXT_ERR_OK
;
547 Security::PeerOptions::updateContextNpn(Security::ContextPtr
&ctx
)
552 #if USE_OPENSSL && defined(TLSEXT_TYPE_next_proto_neg)
553 SSL_CTX_set_next_proto_select_cb(ctx
, &ssl_next_proto_cb
, nullptr);
556 // NOTE: GnuTLS does not support the obsolete NPN extension.
557 // it does support ALPN per-session, not per-context.
561 Security::PeerOptions::updateContextCa(Security::ContextPtr
&ctx
)
563 debugs(83, 8, "Setting CA certificate locations.");
565 for (auto i
: caFiles
) {
567 if (!SSL_CTX_load_verify_locations(ctx
, i
.c_str(), caDir
.c_str())) {
568 const int ssl_error
= ERR_get_error();
569 debugs(83, DBG_IMPORTANT
, "WARNING: Ignoring error setting CA certificate locations: " << ERR_error_string(ssl_error
, NULL
));
572 if (gnutls_certificate_set_x509_trust_file(ctx
, i
.c_str(), GNUTLS_X509_FMT_PEM
) < 0) {
573 debugs(83, DBG_IMPORTANT
, "WARNING: Ignoring error setting CA certificate location: " << i
);
578 if (!flags
.tlsDefaultCa
)
582 if (!SSL_CTX_set_default_verify_paths(ctx
)) {
583 const int ssl_error
= ERR_get_error();
584 debugs(83, DBG_IMPORTANT
, "WARNING: Ignoring error setting default trusted CA : "
585 << ERR_error_string(ssl_error
, NULL
));
588 if (gnutls_certificate_set_x509_system_trust(ctx
) != GNUTLS_E_SUCCESS
) {
589 debugs(83, DBG_IMPORTANT
, "WARNING: Ignoring error setting default trusted CA.");
595 Security::PeerOptions::updateContextCrl(Security::ContextPtr
&ctx
)
598 bool verifyCrl
= false;
599 X509_STORE
*st
= SSL_CTX_get_cert_store(ctx
);
600 if (parsedCrl
.size()) {
601 for (auto &i
: parsedCrl
) {
602 if (!X509_STORE_add_crl(st
, i
.get()))
603 debugs(83, 2, "WARNING: Failed to add CRL");
609 #if X509_V_FLAG_CRL_CHECK
610 if ((parsedFlags
& SSL_FLAG_VERIFY_CRL_ALL
))
611 X509_STORE_set_flags(st
, X509_V_FLAG_CRL_CHECK
|X509_V_FLAG_CRL_CHECK_ALL
);
612 else if (verifyCrl
|| (parsedFlags
& SSL_FLAG_VERIFY_CRL
))
613 X509_STORE_set_flags(st
, X509_V_FLAG_CRL_CHECK
);
616 #endif /* USE_OPENSSL */
620 parse_securePeerOptions(Security::PeerOptions
*opt
)
622 while(const char *token
= ConfigParser::NextToken())