From: Christos Tsantilas Date: Fri, 27 Jan 2017 16:14:19 +0000 (+1300) Subject: Mitigate DoS attacks that use client-initiated SSL/TLS renegotiation. X-Git-Tag: SQUID_3_5_24~3 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=9b18d6d7e727f9a7143a21a49bf4442b07ef049b;p=thirdparty%2Fsquid.git 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. --- diff --git a/src/Makefile.am b/src/Makefile.am index f38f690ee9..f427d1c51d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1673,6 +1673,7 @@ tests_testDiskIO_SOURCES = \ tests/stub_ETag.cc \ EventLoop.cc \ event.cc \ + FadingCounter.cc \ fatal.h \ tests/stub_fatal.cc \ fd.h \ @@ -3177,6 +3178,7 @@ tests_testUfs_SOURCES = \ store_rebuild.h \ tests/stub_store_rebuild.cc \ tests/stub_store_stats.cc \ + FadingCounter.cc \ fatal.h \ tests/stub_fatal.cc \ fd.h \ @@ -3359,6 +3361,7 @@ tests_testRock_SOURCES = \ ETag.cc \ EventLoop.cc \ event.cc \ + FadingCounter.cc \ fatal.h \ fatal.cc \ fd.h \ diff --git a/src/ssl/bio.cc b/src/ssl/bio.cc index af6f1e7c0c..6347a6756f 100644 --- a/src/ssl/bio.cc +++ b/src/ssl/bio.cc @@ -175,6 +175,16 @@ Ssl::Bio::prepReadBuf() 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) { @@ -194,11 +204,32 @@ 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; @@ -222,6 +253,12 @@ const char *objToString(unsigned char const *bytes, int len) 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) diff --git a/src/ssl/bio.h b/src/ssl/bio.h index 4d1ea8968a..7ed7c11e38 100644 --- a/src/ssl/bio.h +++ b/src/ssl/bio.h @@ -9,6 +9,7 @@ #ifndef SQUID_SSL_BIO_H #define SQUID_SSL_BIO_H +#include "FadingCounter.h" #include "fd.h" #include "SBuf.h" @@ -134,7 +135,7 @@ 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 @@ -156,11 +157,22 @@ public: 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 diff --git a/src/ssl/support.cc b/src/ssl/support.cc index 7451299720..1e672b338e 100644 --- a/src/ssl/support.cc +++ b/src/ssl/support.cc @@ -848,18 +848,6 @@ Ssl::readDHParams(const char *dhfile) 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) { @@ -889,10 +877,6 @@ 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)); @@ -1261,10 +1245,6 @@ sslCreateClientContext(const char *certfile, const char *keyfile, int version, c 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 << ".");