[squid-dev] [PATCH] TLS NPN updates
Amos Jeffries
squid3 at treenet.co.nz
Wed Dec 16 05:20:19 UTC 2015
Would help to actually attach the patch I suppose :-P
On 16/12/2015 6:19 p.m., Amos Jeffries wrote:
> This patch is shuffling the TLS NPN gadgetry into libsecurity.
>
> It would be a non-audit commit except that ...
>
> * there is a new config option added (tls-no-npn) to fully disable NPN
> on selected peers or ports.
>
> * ICAPS connections are setting that option by default to prevent NPN
> wrongly advertising them as HTTPS connections.
>
>
> If there are no objections to those two small changes I would like to
> fast-track this patch and apply in ~48hrs.
>
Amos
-------------- next part --------------
=== modified file 'doc/release-notes/release-4.sgml'
--- doc/release-notes/release-4.sgml 2015-11-28 14:18:32 +0000
+++ doc/release-notes/release-4.sgml 2015-12-15 23:29:25 +0000
@@ -154,70 +154,73 @@
<p>New tag to define TLS security context options for outgoing
connections. For example to HTTPS servers.
<tag>url_rewrite_timeout</tag>
<p>Squid times active requests to redirector. This option sets
the timeout value and the Squid reaction to a timed out
request.
</descrip>
<sect1>Changes to existing tags<label id="modifiedtags">
<p>
<descrip>
<tag>auth_param</tag>
<p>New parameter <em>queue-size=</em> to set the maximum number
of queued requests.
<tag>cache_peer</tag>
<p>New option <em>tls-min-version=1.N</em> to set minimum TLS version allowed.
<p>New option <em>tls-no-default-ca</em> replaces <em>sslflags=NO_DEFAULT_CA</em>
+ <p>New option <em>tls-no-npn</em> to disable sending TLS NPN extension.
<p>All <em>ssloptions=</em> values for SSLv2 configuration or disabling
have been removed.
<p>Removed <em>sslversion=</em> option. Use <em>tls-options=</em> instead.
<p>Manual squid.conf update may be required on upgrade.
<p>Replaced <em>sslcafile=</em> with <em>tls-cafile=</em> which takes multiple entries.
<tag>external_acl_type</tag>
<p>New parameter <em>queue-size=</em> to set the maximum number
of queued requests.
<p>Format field updated to accept any logformat %macro code.
<tag>http_port</tag>
<p>New option <em>tls-min-version=1.N</em> to set minimum TLS version allowed.
<p>New option <em>tls-no-default-ca</em> replaces <em>sslflags=NO_DEFAULT_CA</em>
+ <p>New option <em>tls-no-npn</em> to disable sending TLS NPN extension.
<p>All <em>option=</em> values for SSLv2 configuration or disabling
have been removed.
<p>Removed <em>version=</em> option. Use <em>tls-options=</em> instead.
<p>New <em>options=SINGLE_ECDH_USE</em> parameter to enable ephemeral
ECDH key exchange.
<p>Deprecated <em>dhparams=</em> option. Use <em>tls-dh=</em> instead.
The new option allows to optionally specify an elliptic curve for
ephemeral ECDH by adding <em>curve-name:</em> in front of the
parameter file name.
<p>Manual squid.conf update may be required on upgrade.
<p>Replaced <em>cafile=</em> with <em>tls-cafile=</em> which takes multiple entries.
<p>New option <em>tls-no-default-ca</em> replaces <em>sslflags=NO_DEFAULT_CA</em>
<tag>https_port</tag>
<p>New option <em>tls-min-version=1.N</em> to set minimum TLS version allowed.
<p>New option <em>tls-no-default-ca</em> replaces <em>sslflags=NO_DEFAULT_CA</em>
+ <p>New option <em>tls-no-npn</em> to disable sending TLS NPN extension.
<p>All <em>options=</em> values for SSLv2
configuration or disabling have been removed.
<p>Removed <em>version=</em> option. Use <em>tls-options=</em> instead.
<p>New <em>options=SINGLE_ECDH_USE</em> parameter to enable ephemeral
ECDH key exchange.
<p>Deprecated <em>dhparams=</em> option. Use <em>tls-dh=</em> instead.
The new option allows to optionally specify an elliptic curve for
ephemeral ECDH by adding <em>curve-name:</em> in front of the
parameter file name.
<p>Manual squid.conf update may be required on upgrade.
<p>Replaced <em>cafile=</em> with <em>tls-cafile=</em> which takes multiple entries.
<tag>icap_service</tag>
<p>New scheme <em>icaps://</em> to enable TLS/SSL connections to Secure ICAP
servers on port 11344.
<p>New <em>tls-cert=</em> option to set TLS client certificate to use.
<p>New <em>tls-key=</em> option to set TLS private key matching the client
certificate used.
<p>New <em>tls-min-version=1.N</em> option to set minimum TLS version allowed
on server connections.
=== modified file 'src/adaptation/ServiceConfig.cc'
--- src/adaptation/ServiceConfig.cc 2015-08-11 04:41:55 +0000
+++ src/adaptation/ServiceConfig.cc 2015-12-16 03:21:24 +0000
@@ -132,40 +132,45 @@
debugs(3, DBG_PARSE_NOTE(DBG_IMPORTANT), "WARNING: adaptation option '" << name << "' requires --with-openssl. ICAP service option ignored.");
#else
// name prefix is "ssl" or "tls-"
std::string tmp = name + (name[0] == 's' ? 3 : 4);
tmp += "=";
tmp += value;
secure.parse(tmp.c_str());
grokked = true;
#endif
} else
grokked = grokExtension(name, value);
if (!grokked)
return false;
}
// set default on-overload value if needed
if (!onOverloadSet)
onOverload = bypass ? srvBypass : srvWait;
+ // disable the TLS NPN extension if encrypted.
+ // Squid advertises "http/1.1", which is wrong for ICAPS.
+ if (secure.encryptTransport)
+ secure.parse("no-npn");
+
// is the service URI set?
if (!grokkedUri) {
debugs(3, DBG_CRITICAL, cfg_filename << ':' << config_lineno << ": " <<
"No \"uri\" option in adaptation service definition");
return false;
}
debugs(3,5, cfg_filename << ':' << config_lineno << ": " <<
"adaptation_service " << key << ' ' <<
methodStr() << "_" << vectPointStr() << ' ' <<
bypass << routing << ' ' <<
uri);
return true;
}
bool
Adaptation::ServiceConfig::grokUri(const char *value)
{
// TODO: find core code that parses URLs and extracts various parts
=== modified file 'src/cf.data.pre'
--- src/cf.data.pre 2015-12-02 19:45:15 +0000
+++ src/cf.data.pre 2015-12-15 23:27:31 +0000
@@ -1958,40 +1958,42 @@
this option is not set.
sslflags= Various flags modifying the use of SSL:
DELAYED_AUTH
Don't request client certificates
immediately, but wait until acl processing
requires a certificate (not yet implemented).
NO_SESSION_REUSE
Don't allow for session reuse. Each connection
will result in a new SSL session.
VERIFY_CRL
Verify CRL lists when accepting client
certificates.
VERIFY_CRL_ALL
Verify CRL lists for all certificates in the
client certificate chain.
tls-no-default-ca
Do not use the system default Trusted CA.
+ tls-no-npn Do not use the TLS NPN extension to advertise HTTP/1.1.
+
sslcontext= SSL session ID context identifier.
Other Options:
connection-auth[=on|off]
use connection-auth=off to tell Squid to prevent
forwarding Microsoft connection oriented authentication
(NTLM, Negotiate and Kerberos)
disable-pmtu-discovery=
Control Path-MTU discovery usage:
off lets OS decide on what to do (default).
transparent disable PMTU discovery when transparent
support is enabled.
always disable always PMTU discovery.
In many setups of transparently intercepting proxies
Path-MTU discovery can not work on traffic towards the
clients. This is the case when the intercepting device
does not fully track connections and fails to forward
@@ -3266,40 +3268,42 @@
DONT_VERIFY_DOMAIN
Don't verify the peer certificate
matches the server name
ssldomain= The peer name as advertised in it's certificate.
Used for verifying the correctness of the received peer
certificate. If not specified the peer hostname will be
used.
front-end-https
Enable the "Front-End-Https: On" header needed when
using Squid as a SSL frontend in front of Microsoft OWA.
See MS KB document Q307347 for details on this header.
If set to auto the header will only be added if the
request is forwarded as a https:// URL.
tls-no-default-ca
Do not use the system default Trusted CA.
+ tls-no-npn Do not use the TLS NPN extension to advertise HTTP/1.1.
+
==== GENERAL OPTIONS ====
connect-timeout=N
A peer-specific connect timeout.
Also see the peer_connect_timeout directive.
connect-fail-limit=N
How many times connecting to a peer must fail before
it is marked as down. Standby connection failures
count towards this limit. Default is 10.
allow-miss Disable Squid's use of only-if-cached when forwarding
requests to siblings. This is primarily useful when
icp_hit_stale is used by the sibling. Excessive use
of this option may result in forwarding loops. One way
to prevent peering loops when using this option, is to
deny cache peer usage on requests from a peer:
acl fromPeer ...
cache_peer_access peerName deny fromPeer
=== modified file 'src/security/PeerOptions.cc'
--- src/security/PeerOptions.cc 2015-12-08 01:48:40 +0000
+++ src/security/PeerOptions.cc 2015-12-16 05:02:33 +0000
@@ -76,40 +76,42 @@
} else if (strncmp(token, "cipher=", 7) == 0) {
sslCipher = SBuf(token + 7);
} else if (strncmp(token, "cafile=", 7) == 0) {
caFiles.emplace_back(SBuf(token + 7));
} else if (strncmp(token, "capath=", 7) == 0) {
caDir = SBuf(token + 7);
#if !USE_OPENSSL
debugs(3, DBG_PARSE_NOTE(1), "WARNING: capath= option requires --with-openssl.");
#endif
} else if (strncmp(token, "crlfile=", 8) == 0) {
crlFile = SBuf(token + 8);
loadCrlFile();
} else if (strncmp(token, "flags=", 6) == 0) {
if (parsedFlags != 0) {
debugs(3, DBG_PARSE_NOTE(1), "WARNING: Overwriting flags=" << sslFlags << " with " << SBuf(token + 6));
}
sslFlags = SBuf(token + 6);
parsedFlags = parseFlags();
} else if (strncmp(token, "domain=", 7) == 0) {
sslDomain = SBuf(token + 7);
+ } else if (strncmp(token, "no-npn", 6) == 0) {
+ flags.noTlsNpn = true;
} else {
debugs(3, DBG_CRITICAL, "ERROR: Unknown TLS option '" << token << "'");
return;
}
encryptTransport = true;
}
void
Security::PeerOptions::dumpCfg(Packable *p, const char *pfx) const
{
if (!encryptTransport) {
p->appendf(" %sdisable", pfx);
return; // no other settings are relevant
}
for (auto &i : certs) {
if (!i.certFile.isEmpty())
p->appendf(" %scert=" SQUIDSBUFPH, pfx, SQUIDSBUFPRINT(i.certFile));
@@ -118,40 +120,43 @@
}
if (!sslOptions.isEmpty())
p->appendf(" %soptions=" SQUIDSBUFPH, pfx, SQUIDSBUFPRINT(sslOptions));
if (!sslCipher.isEmpty())
p->appendf(" %scipher=" SQUIDSBUFPH, pfx, SQUIDSBUFPRINT(sslCipher));
for (auto i : caFiles) {
p->appendf(" %scafile=" SQUIDSBUFPH, pfx, SQUIDSBUFPRINT(i));
}
if (!caDir.isEmpty())
p->appendf(" %scapath=" SQUIDSBUFPH, pfx, SQUIDSBUFPRINT(caDir));
if (!crlFile.isEmpty())
p->appendf(" %scrlfile=" SQUIDSBUFPH, pfx, SQUIDSBUFPRINT(crlFile));
if (!sslFlags.isEmpty())
p->appendf(" %sflags=" SQUIDSBUFPH, pfx, SQUIDSBUFPRINT(sslFlags));
+
+ if (flags.noTlsNpn)
+ p->appendf(" %sno-npn", pfx);
}
void
Security::PeerOptions::updateTlsVersionLimits()
{
if (!tlsMinVersion.isEmpty()) {
::Parser::Tokenizer tok(tlsMinVersion);
int64_t v = 0;
if (tok.skip('1') && tok.skip('.') && tok.int64(v, 10, false, 1) && v <= 3) {
// only account for TLS here - SSL versions are handled by options= parameter
// avoid affecting options= parameter in cachemgr config report
#if SSL_OP_NO_TLSv1
if (v > 0)
parsedOptions |= SSL_OP_NO_TLSv1;
#endif
#if SSL_OP_NO_TLSv1_1
if (v > 1)
parsedOptions |= SSL_OP_NO_TLSv1_1;
#endif
#if SSL_OP_NO_TLSv1_2
@@ -226,40 +231,41 @@
return t;
}
Security::ContextPtr
Security::PeerOptions::createClientContext(bool setOptions)
{
Security::ContextPtr t = nullptr;
updateTlsVersionLimits();
#if USE_OPENSSL
// XXX: temporary performance regression. c_str() data copies and prevents this being a const method
t = sslCreateClientContext(*this, (setOptions ? parsedOptions : 0), parsedFlags);
#elif USE_GNUTLS && WHEN_READY_FOR_GNUTLS
t = createBlankContext();
#endif
if (t) {
+ updateContextNpn(t);
updateContextCa(t);
updateContextCrl(t);
}
return t;
}
/// set of options we can parse and what they map to
static struct ssl_option {
const char *name;
long value;
} ssl_options[] = {
#if SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG
{
"NETSCAPE_REUSE_CIPHER_CHANGE_BUG", SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG
},
#endif
#if SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG
@@ -506,40 +512,65 @@
Security::PeerOptions::loadCrlFile()
{
parsedCrl.clear();
if (crlFile.isEmpty())
return;
#if USE_OPENSSL
BIO *in = BIO_new_file(crlFile.c_str(), "r");
if (!in) {
debugs(83, 2, "WARNING: Failed to open CRL file " << crlFile);
return;
}
while (X509_CRL *crl = PEM_read_bio_X509_CRL(in,NULL,NULL,NULL)) {
parsedCrl.emplace_back(Security::CrlPointer(crl));
}
BIO_free(in);
#endif
}
+#if USE_OPENSSL && defined(TLSEXT_TYPE_next_proto_neg)
+// Dummy next_proto_neg callback
+static int
+ssl_next_proto_cb(SSL *s, unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, void *arg)
+{
+ static const unsigned char supported_protos[] = {8, 'h','t','t', 'p', '/', '1', '.', '1'};
+ (void)SSL_select_next_proto(out, outlen, in, inlen, supported_protos, sizeof(supported_protos));
+ return SSL_TLSEXT_ERR_OK;
+}
+#endif
+
+void
+Security::PeerOptions::updateContextNpn(Security::ContextPtr &ctx)
+{
+ if (flags.noTlsNpn)
+ return;
+
+#if USE_OPENSSL && defined(TLSEXT_TYPE_next_proto_neg)
+ SSL_CTX_set_next_proto_select_cb(ctx, &ssl_next_proto_cb, nullptr);
+#endif
+
+ // NOTE: GnuTLS does not support the obsolete NPN extension.
+ // it does support ALPN per-session, not per-context.
+}
+
void
Security::PeerOptions::updateContextCa(Security::ContextPtr &ctx)
{
debugs(83, 8, "Setting CA certificate locations.");
for (auto i : caFiles) {
#if USE_OPENSSL
if (!SSL_CTX_load_verify_locations(ctx, i.c_str(), caDir.c_str())) {
const int ssl_error = ERR_get_error();
debugs(83, DBG_IMPORTANT, "WARNING: Ignoring error setting CA certificate locations: " << ERR_error_string(ssl_error, NULL));
}
#elif USE_GNUTLS
if (gnutls_certificate_set_x509_trust_file(ctx, i.c_str(), GNUTLS_X509_FMT_PEM) < 0) {
debugs(83, DBG_IMPORTANT, "WARNING: Ignoring error setting CA certificate location: " << i);
}
#endif
}
if (flags.noDefaultCa)
return;
=== modified file 'src/security/PeerOptions.h'
--- src/security/PeerOptions.h 2015-12-08 01:48:40 +0000
+++ src/security/PeerOptions.h 2015-12-15 23:21:30 +0000
@@ -23,80 +23,86 @@
public:
PeerOptions() : parsedOptions(0), parsedFlags(0), sslVersion(0), encryptTransport(false) {}
PeerOptions(const PeerOptions &);
virtual ~PeerOptions() = default;
/// parse a TLS squid.conf option
virtual void parse(const char *);
/// reset the configuration details to default
virtual void clear() {*this = PeerOptions();}
/// generate an unset security context object
virtual Security::ContextPtr createBlankContext() const;
/// generate a security client-context from these configured options
Security::ContextPtr createClientContext(bool setOptions);
/// sync the context options with tls-min-version=N configuration
void updateTlsVersionLimits();
+ /// setup the NPN extension details for the given context
+ void updateContextNpn(Security::ContextPtr &);
+
/// setup the CA details for the given context
void updateContextCa(Security::ContextPtr &);
/// setup the CRL details for the given context
void updateContextCrl(Security::ContextPtr &);
/// output squid.conf syntax with 'pfx' prefix on parameters for the stored settings
virtual void dumpCfg(Packable *, const char *pfx) const;
private:
long parseOptions();
long parseFlags();
void loadCrlFile();
public:
SBuf sslOptions; ///< library-specific options string
SBuf caDir; ///< path of directory containing a set of trusted Certificate Authorities
SBuf crlFile; ///< path of file containing Certificate Revoke List
SBuf sslCipher;
SBuf sslFlags; ///< flags defining what TLS operations Squid performs
SBuf sslDomain;
SBuf tlsMinVersion; ///< version label for minimum TLS version to permit
long parsedOptions; ///< parsed value of sslOptions
long parsedFlags; ///< parsed value of sslFlags
std::list<Security::KeyData> certs; ///< details from the cert= and file= config parameters
std::list<SBuf> caFiles; ///< paths of files containing trusted Certificate Authority
Security::CertRevokeList parsedCrl; ///< CRL to use when verifying the remote end certificate
private:
int sslVersion;
/// flags governing Squid internal TLS operations
struct flags_ {
- flags_() : noDefaultCa(false) {}
+ flags_() : noDefaultCa(false), noTlsNpn(false) {}
/// do not use the system default Trusted CA when verifying the remote end certificate
bool noDefaultCa;
+
+ /// do not use the TLS NPN extension on these connections
+ bool noTlsNpn;
} flags;
public:
/// whether transport encryption (TLS/SSL) is to be used on connections to the peer
bool encryptTransport;
};
/// configuration options for DIRECT server access
extern PeerOptions ProxyOutgoingConfig;
} // namespace Security
// parse the tls_outgoing_options directive
void parse_securePeerOptions(Security::PeerOptions *);
#define free_securePeerOptions(x) Security::ProxyOutgoingConfig.clear()
#define dump_securePeerOptions(e,n,x) do { (e)->appendf(n); (x).dumpCfg((e),""); (e)->append("\n",1); } while(false)
#endif /* SQUID_SRC_SECURITY_PEEROPTIONS_H */
=== modified file 'src/ssl/support.cc'
--- src/ssl/support.cc 2015-12-13 17:53:57 +0000
+++ src/ssl/support.cc 2015-12-15 23:15:30 +0000
@@ -605,51 +605,40 @@
debugs(83, 5, "Comparing private and public SSL keys.");
if (!SSL_CTX_check_private_key(sslContext)) {
ssl_error = ERR_get_error();
debugs(83, DBG_CRITICAL, "ERROR: SSL private key '" << certfile << "' does not match public key '" <<
keyfile << "': " << ERR_error_string(ssl_error, NULL));
SSL_CTX_free(sslContext);
return NULL;
}
*/
if (!configureSslContext(sslContext, port)) {
debugs(83, DBG_CRITICAL, "ERROR: Configuring static SSL context");
SSL_CTX_free(sslContext);
return NULL;
}
return sslContext;
}
-#if defined(TLSEXT_TYPE_next_proto_neg)
-//Dummy next_proto_neg callback
-static int
-ssl_next_proto_cb(SSL *s, unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, void *arg)
-{
- static const unsigned char supported_protos[] = {8, 'h','t','t', 'p', '/', '1', '.', '1'};
- (void)SSL_select_next_proto(out, outlen, in, inlen, supported_protos, sizeof(supported_protos));
- return SSL_TLSEXT_ERR_OK;
-}
-#endif
-
Security::ContextPtr
sslCreateClientContext(Security::PeerOptions &peer, long options, long fl)
{
Security::ContextPtr sslContext(peer.createBlankContext());
if (!sslContext)
return nullptr;
SSL_CTX_set_options(sslContext, options);
#if defined(SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS)
SSL_CTX_set_info_callback(sslContext, ssl_info_cb);
#endif
if (!peer.sslCipher.isEmpty()) {
debugs(83, 5, "Using chiper suite " << peer.sslCipher << ".");
const char *cipher = peer.sslCipher.c_str();
if (!SSL_CTX_set_cipher_list(sslContext, cipher)) {
const int ssl_error = ERR_get_error();
fatalf("Failed to set SSL cipher suite '%s': %s\n",
@@ -682,43 +671,40 @@
debugs(83, 5, "Comparing private and public SSL keys.");
if (!SSL_CTX_check_private_key(sslContext)) {
const int ssl_error = ERR_get_error();
fatalf("SSL private key '%s' does not match public key '%s': %s\n",
certfile, keyfile, ERR_error_string(ssl_error, NULL));
}
}
debugs(83, 9, "Setting RSA key generation callback.");
SSL_CTX_set_tmp_rsa_callback(sslContext, ssl_temp_rsa_cb);
if (fl & SSL_FLAG_DONT_VERIFY_PEER) {
debugs(83, 2, "NOTICE: Peer certificates are not verified for validity!");
SSL_CTX_set_verify(sslContext, SSL_VERIFY_NONE, NULL);
} else {
debugs(83, 9, "Setting certificate verification callback.");
SSL_CTX_set_verify(sslContext, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, ssl_verify_cb);
}
-#if defined(TLSEXT_TYPE_next_proto_neg)
- SSL_CTX_set_next_proto_select_cb(sslContext, &ssl_next_proto_cb, NULL);
-#endif
return sslContext;
}
/// \ingroup ServerProtocolSSLInternal
int
ssl_read_method(int fd, char *buf, int len)
{
SSL *ssl = fd_table[fd].ssl;
int i;
#if DONT_DO_THIS
if (!SSL_is_init_finished(ssl)) {
errno = ENOTCONN;
return -1;
}
#endif
i = SSL_read(ssl, buf, len);
More information about the squid-dev
mailing list