The patches r15016 and r15017 requires to allow make check/distcheck
work in some platoforms.
I am attaching a new patch for squid-3.5.
On 24/01/2017 02:55 μμ, Christos Tsantilas wrote:
The t3 patch applied to squid-5 as r15014
I am also attaching the patch for squid-3.5.
On 23/01/2017 03:52 μμ, Amos Jeffries wrote:
On 23/01/2017 11:04 p.m., Christos Tsantilas wrote:
On 22/01/2017 07:11 μμ, Amos Jeffries wrote:
On 23/01/2017 1:03 a.m., Christos Tsantilas wrote:
There is a well-known DoS attack using client-initiated SSL/TLS
renegotiation. The severity or uniqueness of this attack method is
disputed, but many believe it is serious/real.
There is even a (disputed) CVE 2011-1473:
https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2011-1473
The old Squid code tried to disable client-initiated renegotiation,
but
it did not work reliably (or at all), depending on Squid version,
due to
OpenSSL API changes and conflicting SslBump callbacks. That code is
now
removed and client-initiated renegotiations are allowed.
With this change, Squid aborts the TLS connection, with a level-1
ERROR
message if the rate of client-initiated renegotiate requests
exceeds 5
requests in 10 seconds (approximately). This protection and the rate
limit are currently hard-coded but the rate is not expected to be
exceeded under normal circumstances.
This is a Measurement Factory project
+1.
Amos
Mitigate DoS attacks that use client-initiated SSL/TLS renegotiation.
There is a well-known DoS attack using client-initiated SSL/TLS
renegotiation. The severety or uniqueness of this attack method
is disputed, but many believe it is serious/real.
There is even a (disputed) CVE 2011-1473:
https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2011-1473
The old Squid code tried to disable client-initiated renegotiation, but
it did not work reliably (or at all), depending on Squid version, due
to OpenSSL API changes and conflicting SslBump callbacks. That
code is now removed and client-initiated renegotiations are allowed.
With this change, Squid aborts the TLS connection, with a level-1 ERROR
message if the rate of client-initiated renegotiate requests exceeds
5 requests in 10 seconds (approximately). This protection and the rate
limit are currently hard-coded but the rate is not expected to be
exceeded under normal circumstances.
This is a Measurement Factory project.
=== modified file 'src/Makefile.am'
--- src/Makefile.am 2017-01-01 00:16:45 +0000
+++ src/Makefile.am 2017-01-25 09:54:46 +0000
@@ -1656,40 +1656,41 @@
tests_testCacheManager_DEPENDENCIES = \
$(REPL_OBJS) \
$(SQUID_CPPUNIT_LA)
tests_testDiskIO_SOURCES = \
CacheDigest.h \
tests/stub_CacheDigest.cc \
cbdata.cc \
client_db.h \
ClientInfo.h \
tests/stub_CollapsedForwarding.cc \
ConfigOption.cc \
ConfigParser.cc \
$(DELAY_POOL_SOURCE) \
$(DISKIO_SOURCE) \
disk.h \
disk.cc \
tests/stub_ETag.cc \
EventLoop.cc \
event.cc \
+ FadingCounter.cc \
fatal.h \
tests/stub_fatal.cc \
fd.h \
fd.cc \
fde.h \
fde.cc \
FileMap.h \
filemap.cc \
HttpBody.h \
HttpBody.cc \
HttpHeaderFieldStat.h \
HttpHdrCc.h \
HttpHdrCc.cc \
HttpHdrCc.cci \
HttpHdrContRange.cc \
HttpHdrSc.cc \
HttpHdrScTarget.cc \
HttpHdrRange.cc \
HttpHeaderFieldInfo.h \
HttpHeaderTools.h \
@@ -3160,40 +3161,41 @@
tests/stub_client_db.cc \
tests/stub_CollapsedForwarding.cc \
tests/stub_HelperChildConfig.cc \
tests/stub_icp.cc \
tests/stub_ipc.cc \
tests/stub_ipcache.cc \
tests/stub_libeui.cc \
tests/stub_libicmp.cc \
tests/stub_MemStore.cc \
tests/stub_neighbors.cc \
tests/stub_pconn.cc \
tests/stub_Port.cc \
tests/stub_UdsOp.cc \
internal.h \
tests/stub_internal.cc \
tests/stub_libformat.cc \
tests/stub_stat.cc \
store_rebuild.h \
tests/stub_store_rebuild.cc \
tests/stub_store_stats.cc \
+ FadingCounter.cc \
fatal.h \
tests/stub_fatal.cc \
fd.h \
fd.cc \
fde.h \
fde.cc \
client_db.h \
disk.h \
disk.cc \
FileMap.h \
filemap.cc \
HttpBody.h \
HttpBody.cc \
HttpReply.cc \
int.h \
int.cc \
RequestFlags.h \
RequestFlags.cc \
SquidList.h \
SquidList.cc \
@@ -3342,40 +3344,41 @@
tests/testRefCount.cc
testRefCount_LDADD = \
base/libbase.la \
$(top_builddir)/lib/libmiscutil.la \
$(COMPAT_LIB) \
$(XTRA_LIBS)
tests_testRock_SOURCES = \
cbdata.cc \
CacheDigest.h \
CollapsedForwarding.h \
CollapsedForwarding.cc \
tests/stub_CacheDigest.cc \
ConfigOption.cc \
ConfigParser.cc \
disk.h \
disk.cc \
ETag.cc \
EventLoop.cc \
event.cc \
+ FadingCounter.cc \
fatal.h \
fatal.cc \
fd.h \
fd.cc \
fde.h \
fde.cc \
FileMap.h \
filemap.cc \
HttpHeaderFieldStat.h \
HttpBody.h \
HttpBody.cc \
HttpHdrCc.cc \
HttpHdrContRange.cc \
HttpHdrRange.cc \
HttpHdrSc.cc \
HttpHdrScTarget.cc \
HttpHeader.h \
HttpHeader.cc \
HttpHeaderFieldInfo.h \
HttpHeaderTools.h \
=== modified file 'src/ssl/bio.cc'
--- src/ssl/bio.cc 2017-01-01 00:16:45 +0000
+++ src/ssl/bio.cc 2017-01-24 12:42:33 +0000
@@ -158,87 +158,124 @@
// Here we can use (where & STATE) to check the current state.
// Many STATE values are possible, including: SSL_CB_CONNECT_LOOP,
// SSL_CB_ACCEPT_LOOP, SSL_CB_HANDSHAKE_START, and SSL_CB_HANDSHAKE_DONE.
// For example:
// if (where & SSL_CB_HANDSHAKE_START)
// debugs(83, 9, "Trying to establish the SSL connection");
// else if (where & SSL_CB_HANDSHAKE_DONE)
// debugs(83, 9, "SSL connection established");
debugs(83, 7, "FD " << fd_ << " now: 0x" << std::hex << where << std::dec << ' ' <<
SSL_state_string(ssl) << " (" << SSL_state_string_long(ssl) << ")");
}
void
Ssl::Bio::prepReadBuf()
{
if (rbuf.isNull())
rbuf.init(4096, 65536);
}
+Ssl::ClientBio::ClientBio(const int anFd):
+ Bio(anFd),
+ holdRead_(false),
+ holdWrite_(false),
+ helloState(atHelloNone),
+ abortReason(nullptr)
+{
+ renegotiations.configure(10*1000);
+}
+
bool
Ssl::ClientBio::isClientHello(int state)
{
return (
#if defined(SSL2_ST_GET_CLIENT_HELLO_A)
state == SSL2_ST_GET_CLIENT_HELLO_A ||
#endif
state == SSL3_ST_SR_CLNT_HELLO_A ||
state == SSL23_ST_SR_CLNT_HELLO_A ||
state == SSL23_ST_SR_CLNT_HELLO_B ||
state == SSL3_ST_SR_CLNT_HELLO_B ||
state == SSL3_ST_SR_CLNT_HELLO_C
);
}
void
Ssl::ClientBio::stateChanged(const SSL *ssl, int where, int ret)
{
Ssl::Bio::stateChanged(ssl, where, ret);
+ // detect client-initiated renegotiations DoS (CVE-2011-1473)
+ if (where & SSL_CB_HANDSHAKE_START) {
+ const int reneg = renegotiations.count(1);
+
+ if (abortReason)
+ return; // already decided and informed the admin
+
+ if (reneg > RenegotiationsLimit) {
+ abortReason = "renegotiate requests flood";
+ debugs(83, DBG_IMPORTANT, "Terminating TLS connection [from " << fd_table[fd_].ipaddr << "] due to " << abortReason << ". This connection received " <<
+ reneg << " renegotiate requests in the last " <<
+ RenegotiationsWindow << " seconds (and " <<
+ renegotiations.remembered() << " requests total).");
+ }
+ }
}
int
Ssl::ClientBio::write(const char *buf, int size, BIO *table)
{
+ if (abortReason) {
+ debugs(83, 3, "BIO on FD " << fd_ << " is aborted");
+ BIO_clear_retry_flags(table);
+ return -1;
+ }
+
if (holdWrite_) {
BIO_set_retry_write(table);
return 0;
}
return Ssl::Bio::write(buf, size, table);
}
const char *objToString(unsigned char const *bytes, int len)
{
static std::string buf;
buf.clear();
for (int i = 0; i < len; i++ ) {
char tmp[3];
snprintf(tmp, sizeof(tmp), "%.2x", bytes[i]);
buf.append(tmp);
}
return buf.c_str();
}
int
Ssl::ClientBio::read(char *buf, int size, BIO *table)
{
+ if (abortReason) {
+ debugs(83, 3, "BIO on FD " << fd_ << " is aborted");
+ BIO_clear_retry_flags(table);
+ return -1;
+ }
+
if (helloState < atHelloReceived) {
int bytes = readAndBuffer(buf, size, table, "TLS client Hello");
if (bytes <= 0)
return bytes;
}
if (helloState == atHelloNone) {
const int helloSize = features.parseMsgHead(rbuf);
if (helloSize == 0) {
// Not enough bytes to get hello message size
BIO_set_retry_read(table);
return -1;
} else if (helloSize < 0) {
return -1;
}
helloState = atHelloStarted; //Next state
}
if (helloState == atHelloStarted) {
=== modified file 'src/ssl/bio.h'
--- src/ssl/bio.h 2017-01-01 00:16:45 +0000
+++ src/ssl/bio.h 2017-01-24 12:46:18 +0000
@@ -1,31 +1,32 @@
/*
* Copyright (C) 1996-2017 The Squid Software Foundation and contributors
*
* Squid software is distributed under GPLv2+ license and includes
* contributions from numerous individuals and organizations.
* Please see the COPYING and CONTRIBUTORS files for details.
*/
#ifndef SQUID_SSL_BIO_H
#define SQUID_SSL_BIO_H
+#include "FadingCounter.h"
#include "fd.h"
#include "SBuf.h"
#include <iosfwd>
#include <list>
#if HAVE_OPENSSL_BIO_H
#include <openssl/bio.h>
#endif
#include <string>
namespace Ssl
{
/// BIO source and sink node, handling socket I/O and monitoring SSL state
class Bio
{
public:
enum Type {
BIO_TO_CLIENT = 6000,
BIO_TO_SERVER
@@ -117,67 +118,78 @@
/// Prepare the rbuf buffer to accept hello data
void prepReadBuf();
/// Reads data from socket and record them to a buffer
int readAndBuffer(char *buf, int size, BIO *table, const char *description);
const MemBuf &rBufData() {return rbuf;}
protected:
const int fd_; ///< the SSL socket we are reading and writing
MemBuf rbuf; ///< Used to buffer input data.
};
/// BIO node to handle socket IO for squid client side
/// If bumping is enabled this Bio detects and analyses client hello message
/// to retrieve the SSL features supported by the client
class ClientBio: public Bio
{
public:
/// The ssl hello message read states
typedef enum {atHelloNone = 0, atHelloStarted, atHelloReceived} HelloReadState;
- explicit ClientBio(const int anFd): Bio(anFd), holdRead_(false), holdWrite_(false), helloState(atHelloNone) {}
+ explicit ClientBio(const int anFd);
/// The ClientBio version of the Ssl::Bio::stateChanged method
/// When the client hello message retrieved, fill the
/// "features" member with the client provided informations.
virtual void stateChanged(const SSL *ssl, int where, int ret);
/// The ClientBio version of the Ssl::Bio::write method
virtual int write(const char *buf, int size, BIO *table);
/// The ClientBio version of the Ssl::Bio::read method
/// If the holdRead flag is true then it does not write any data
/// to socket and sets the "read retry" flag of the BIO to true
virtual int read(char *buf, int size, BIO *table);
/// Return true if the client hello message received and analized
bool gotHello() { return (helloState == atHelloReceived); }
/// Return the SSL features requested by SSL client
const Bio::sslFeatures &getFeatures() const {return features;}
/// Prevents or allow writting on socket.
void hold(bool h) {holdRead_ = holdWrite_ = h;}
private:
/// True if the SSL state corresponds to a hello message
bool isClientHello(int state);
+
+ /// approximate size of a time window for computing client-initiated renegotiation rate (in seconds)
+ static const time_t RenegotiationsWindow = 10;
+
+ /// the maximum tolerated number of client-initiated renegotiations in RenegotiationsWindow
+ static const int RenegotiationsLimit = 5;
+
/// The futures retrieved from client SSL hello message
Bio::sslFeatures features;
bool holdRead_; ///< The read hold state of the bio.
bool holdWrite_; ///< The write hold state of the bio.
HelloReadState helloState; ///< The SSL hello read state
+ FadingCounter renegotiations; ///< client requested renegotiations limit control
+
+ /// why we should terminate the connection during next TLS operation (or nil)
+ const char *abortReason;
};
/// BIO node to handle socket IO for squid server side
/// If bumping is enabled, analyses the SSL hello message sent by squid OpenSSL
/// subsystem (step3 bumping step) against bumping mode:
/// * Peek mode: Send client hello message instead of the openSSL generated
/// hello message and normaly denies bumping and allow only
/// splice or terminate the SSL connection
/// * Stare mode: Sends the openSSL generated hello message and normaly
/// denies splicing and allow bump or terminate the SSL
/// connection
/// If SQUID_USE_OPENSSL_HELLO_OVERWRITE_HACK is enabled also checks if the
/// openSSL library features are compatible with the features reported in
/// web client SSL hello message and if it is, overwrites the openSSL SSL
/// object members to replace hello message with web client hello message.
/// This is may allow bumping in peek mode and splicing in stare mode after
/// the server hello message received.
class ServerBio: public Bio
{
public:
=== modified file 'src/ssl/support.cc'
--- src/ssl/support.cc 2017-01-01 00:16:45 +0000
+++ src/ssl/support.cc 2017-01-24 12:43:44 +0000
@@ -831,85 +831,69 @@
DH *dh = NULL;
int codes;
if (in) {
dh = PEM_read_DHparams(in, NULL, NULL, NULL);
fclose(in);
}
if (!dh)
debugs(83, DBG_IMPORTANT, "WARNING: Failed to read DH parameters '" << dhfile << "'");
else if (dh && DH_check(dh, &codes) == 0) {
if (codes) {
debugs(83, DBG_IMPORTANT, "WARNING: Failed to verify DH parameters '" << dhfile << "' (" << std::hex << codes << ")");
DH_free(dh);
dh = NULL;
}
}
return dh;
}
-#if defined(SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS)
-static void
-ssl_info_cb(const SSL *ssl, int where, int ret)
-{
- (void)ret;
- if ((where & SSL_CB_HANDSHAKE_DONE) != 0) {
- // disable renegotiation (CVE-2009-3555)
- ssl->s3->flags |= SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS;
- }
-}
-#endif
-
static bool
configureSslEECDH(SSL_CTX *sslContext, const char *curve)
{
#if OPENSSL_VERSION_NUMBER >= 0x0090800fL && !defined(OPENSSL_NO_ECDH)
int nid = OBJ_sn2nid(curve);
if (!nid) {
debugs(83, DBG_CRITICAL, "ERROR: Unknown EECDH curve '" << curve << "'");
return false;
}
EC_KEY *ecdh = EC_KEY_new_by_curve_name(nid);
if (ecdh == NULL)
return false;
const bool ok = SSL_CTX_set_tmp_ecdh(sslContext, ecdh) != 0;
EC_KEY_free(ecdh);
return ok;
#else
debugs(83, DBG_CRITICAL, "ERROR: EECDH is not available in this build. Please link against OpenSSL>=0.9.8 and ensure OPENSSL_NO_ECDH is not set.");
return false;
#endif
}
static bool
configureSslContext(SSL_CTX *sslContext, AnyP::PortCfg &port)
{
int ssl_error;
SSL_CTX_set_options(sslContext, port.sslOptions);
-#if defined(SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS)
- SSL_CTX_set_info_callback(sslContext, ssl_info_cb);
-#endif
-
if (port.sslContextSessionId)
SSL_CTX_set_session_id_context(sslContext, (const unsigned char *)port.sslContextSessionId, strlen(port.sslContextSessionId));
if (port.sslContextFlags & SSL_FLAG_NO_SESSION_REUSE) {
SSL_CTX_set_session_cache_mode(sslContext, SSL_SESS_CACHE_OFF);
}
if (Config.SSL.unclean_shutdown) {
debugs(83, 5, "Enabling quiet SSL shutdowns (RFC violation).");
SSL_CTX_set_quiet_shutdown(sslContext, 1);
}
if (port.cipher) {
debugs(83, 5, "Using chiper suite " << port.cipher << ".");
if (!SSL_CTX_set_cipher_list(sslContext, port.cipher)) {
ssl_error = ERR_get_error();
debugs(83, DBG_CRITICAL, "ERROR: Failed to set SSL cipher suite '" << port.cipher << "': " << ERR_error_string(ssl_error, NULL));
return false;
@@ -1244,44 +1228,40 @@
if (!keyfile)
keyfile = certfile;
if (!certfile)
certfile = keyfile;
if (!(method = Ssl::method(version)))
return NULL;
sslContext = SSL_CTX_new(method);
if (sslContext == NULL) {
ssl_error = ERR_get_error();
fatalf("Failed to allocate SSL context: %s\n",
ERR_error_string(ssl_error, NULL));
}
SSL_CTX_set_options(sslContext, Ssl::parse_options(options));
-#if defined(SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS)
- SSL_CTX_set_info_callback(sslContext, ssl_info_cb);
-#endif
-
if (cipher) {
debugs(83, 5, "Using chiper suite " << cipher << ".");
if (!SSL_CTX_set_cipher_list(sslContext, cipher)) {
ssl_error = ERR_get_error();
fatalf("Failed to set SSL cipher suite '%s': %s\n",
cipher, ERR_error_string(ssl_error, NULL));
}
}
if (certfile) {
debugs(83, DBG_IMPORTANT, "Using certificate in " << certfile);
if (!SSL_CTX_use_certificate_chain_file(sslContext, certfile)) {
ssl_error = ERR_get_error();
fatalf("Failed to acquire SSL certificate '%s': %s\n",
certfile, ERR_error_string(ssl_error, NULL));
}
debugs(83, DBG_IMPORTANT, "Using private key in " << keyfile);
_______________________________________________
squid-dev mailing list
squid-dev@lists.squid-cache.org
http://lists.squid-cache.org/listinfo/squid-dev