[Pound/f20] Backport various security fixes.

Lubomir Rintel lkundrak at fedoraproject.org
Fri Oct 24 15:20:13 UTC 2014


commit 98f9ab23feeebd518e2bfcf57ba829386fc760a9
Author: Lubomir Rintel <lkundrak at v3.sk>
Date:   Fri Oct 24 17:20:00 2014 +0200

    Backport various security fixes.
    
    Note they usually are extra options that need
    to be enabled manually so that we won't break functionality:
    
    - CVE-2011-3389: Make it possible to deny use of "BEAST" vulnerable ciphers
    - CVE-2012-4929: Disable compression to be safe from "CRIME"
    - CVE-2005-2090: Chunked encofing response splitting (no awkward name here)
    - CVE-2014-3566: Allow disabling SSLv3 (and others), to be safe from "POODLE"
    - A redirect XSS fix

 ...89-BEAST-Add-options-preventing-users-fro.patch |  381 ++++++++++++++++++++
 ...E-2012-4929-CRIME-Disable-SSL-compression.patch |   88 +++++
 0003-Fix-a-XSS-with-a-crafted-URL.patch            |   80 ++++
 ...-2090-chunked-encoding-response-splitting.patch |   81 +++++
 ...3566-POODLE-Backport-Disable-proto-option.patch |   86 +++++
 Pound.spec                                         |   32 ++-
 6 files changed, 747 insertions(+), 1 deletions(-)
---
diff --git a/0001-CVE-2011-3389-BEAST-Add-options-preventing-users-fro.patch b/0001-CVE-2011-3389-BEAST-Add-options-preventing-users-fro.patch
new file mode 100644
index 0000000..e3c95fe
--- /dev/null
+++ b/0001-CVE-2011-3389-BEAST-Add-options-preventing-users-fro.patch
@@ -0,0 +1,381 @@
+From f05affa371ce557c19ddba4aab5b810612ecd694 Mon Sep 17 00:00:00 2001
+From: Joe Gooch <mrwizard at k12system.com>
+Date: Mon, 6 Feb 2012 15:21:10 -0500
+Subject: [PATCH 1/5] CVE-2011-3389 BEAST Add options preventing users from
+ choosing unsafe ciphers
+
+SSL Renegotiation Patch for v2.6 final.
+
+This patch adds two new config directives:
+SSLHonorCipherOrder 0|1
+  When set to 1, server prefers Ciphers in the order specified.  When 0, Server advertises no preference.
+
+SSLAllowClientRenegotiation 0|1|2
+  When set to 0, no client renegotiation will be honored.  When 1, secure renegotiation will be honored.  When 2, insecure renegotiation will be honored.
+
+It will also disable insecure renegotiation on backend HTTPS connections.
+
+Given these options, the most secure configuration would be:
+SSLAllowClientRenegotiation 0
+SSLHonorCipherOrder 1
+Ciphers         "ECDHE-RSA-AES256-SHA384:AES256-SHA256:RC4:HIGH:!MD5:!aNULL:!EDH:!AESGCM"
+
+Which mitigates BEAST attacks as outlined here:
+http://blog.ivanristic.com/2011/10/mitigating-the-beast-attack-on-tls.html
+
+As well as renegotiation attacks.
+
+Test your server at https://www.ssllabs.com/ssldb/
+
+[Picked from https://github.com/goochjj/pound/commit/c32f064fee73783142835ac11c0595d7bcd24d35]
+---
+ config.c | 39 +++++++++++++++++++++++++++++++++++++--
+ http.c   | 45 ++++++++++++++++++++++++++++++++++++++-------
+ pound.8  | 13 +++++++++++++
+ pound.h  |  9 +++++++++
+ svc.c    | 31 +++++++++++++++++++++++++++++++
+ 5 files changed, 128 insertions(+), 9 deletions(-)
+
+diff --git a/config.c b/config.c
+index 1294d84..b066dd8 100755
+--- a/config.c
++++ b/config.c
+@@ -76,7 +76,7 @@ static regex_t  ListenHTTP, ListenHTTPS, End, Address, Port, Cert, xHTTP, Client
+ static regex_t  Err414, Err500, Err501, Err503, MaxRequest, HeadRemove, RewriteLocation, RewriteDestination;
+ static regex_t  Service, ServiceName, URL, HeadRequire, HeadDeny, BackEnd, Emergency, Priority, HAport, HAportAddr;
+ static regex_t  Redirect, RedirectN, TimeOut, Session, Type, TTL, ID, DynScale;
+-static regex_t  ClientCert, AddHeader, Ciphers, CAlist, VerifyList, CRLlist, NoHTTPS11;
++static regex_t  ClientCert, AddHeader, SSLAllowClientRenegotiation, SSLHonorCipherOrder, Ciphers, CAlist, VerifyList, CRLlist, NoHTTPS11;
+ static regex_t  Grace, Include, ConnTO, IgnoreCase, HTTPS, HTTPSCert, Disabled, Threads, CNName;
+ 
+ static regmatch_t   matches[5];
+@@ -289,9 +289,12 @@ parse_be(const int is_emergency)
+         } else if(!regexec(&HTTPS, lin, 4, matches, 0)) {
+             if((res->ctx = SSL_CTX_new(SSLv23_client_method())) == NULL)
+                 conf_err("SSL_CTX_new failed - aborted");
++            SSL_CTX_set_app_data(res->ctx, res);
+             SSL_CTX_set_verify(res->ctx, SSL_VERIFY_NONE, NULL);
+             SSL_CTX_set_mode(res->ctx, SSL_MODE_AUTO_RETRY);
+             SSL_CTX_set_options(res->ctx, SSL_OP_ALL);
++            SSL_CTX_clear_options(res->ctx, SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION);
++            SSL_CTX_clear_options(res->ctx, SSL_OP_LEGACY_SERVER_CONNECT);
+             sprintf(lin, "%d-Pound-%ld", getpid(), random());
+             SSL_CTX_set_session_id_context(res->ctx, (unsigned char *)lin, strlen(lin));
+             SSL_CTX_set_tmp_rsa_callback(res->ctx, RSA_tmp_callback);
+@@ -299,6 +302,7 @@ parse_be(const int is_emergency)
+         } else if(!regexec(&HTTPSCert, lin, 4, matches, 0)) {
+             if((res->ctx = SSL_CTX_new(SSLv23_client_method())) == NULL)
+                 conf_err("SSL_CTX_new failed - aborted");
++            SSL_CTX_set_app_data(res->ctx, res);
+             lin[matches[1].rm_eo] = '\0';
+             if(SSL_CTX_use_certificate_chain_file(res->ctx, lin + matches[1].rm_so) != 1)
+                 conf_err("SSL_CTX_use_certificate_chain_file failed - aborted");
+@@ -309,6 +313,8 @@ parse_be(const int is_emergency)
+             SSL_CTX_set_verify(res->ctx, SSL_VERIFY_NONE, NULL);
+             SSL_CTX_set_mode(res->ctx, SSL_MODE_AUTO_RETRY);
+             SSL_CTX_set_options(res->ctx, SSL_OP_ALL);
++            SSL_CTX_clear_options(res->ctx, SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION);
++            SSL_CTX_clear_options(res->ctx, SSL_OP_LEGACY_SERVER_CONNECT);
+             sprintf(lin, "%d-Pound-%ld", getpid(), random());
+             SSL_CTX_set_session_id_context(res->ctx, (unsigned char *)lin, strlen(lin));
+             SSL_CTX_set_tmp_rsa_callback(res->ctx, RSA_tmp_callback);
+@@ -829,11 +835,15 @@ parse_HTTPS(void)
+     SERVICE     *svc;
+     MATCHER     *m;
+     int         has_addr, has_port, has_other;
++    long	ssl_op_enable, ssl_op_disable;
+     struct hostent      *host;
+     struct sockaddr_in  in;
+     struct sockaddr_in6 in6;
+     POUND_CTX   *pc;
+ 
++    ssl_op_enable = SSL_OP_ALL;
++    ssl_op_disable = SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION | SSL_OP_LEGACY_SERVER_CONNECT;
++
+     if((res = (LISTENER *)malloc(sizeof(LISTENER))) == NULL)
+         conf_err("ListenHTTPS config: out of memory - aborted");
+     memset(res, 0, sizeof(LISTENER));
+@@ -844,6 +854,7 @@ parse_HTTPS(void)
+     res->err500 = "An internal server error occurred. Please try again later.";
+     res->err501 = "This method may not be used.";
+     res->err503 = "The service is not available. Please try again later.";
++    res->allow_cl_reneg = 0;
+     res->log_level = log_level;
+     if(regcomp(&res->verb, xhttp[0], REG_ICASE | REG_NEWLINE | REG_EXTENDED))
+         conf_err("xHTTP bad default pattern - aborted");
+@@ -1029,6 +1040,23 @@ parse_HTTPS(void)
+                 strcat(res->add_head, "\r\n");
+                 strcat(res->add_head, lin + matches[1].rm_so);
+             }
++        } else if(!regexec(&SSLAllowClientRenegotiation, lin, 4, matches, 0)) {
++            res->allow_cl_reneg = atoi(lin + matches[1].rm_so);
++            if (res->allow_cl_reneg == 2) {
++                ssl_op_enable |= SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION;
++                ssl_op_disable &= ~SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION;
++            } else {
++                ssl_op_disable |= SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION;
++                ssl_op_enable &= ~SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION;
++            }
++        } else if(!regexec(&SSLHonorCipherOrder, lin, 4, matches, 0)) {
++            if (atoi(lin + matches[1].rm_so)) {
++                ssl_op_enable |= SSL_OP_CIPHER_SERVER_PREFERENCE;
++                ssl_op_disable &= ~SSL_OP_CIPHER_SERVER_PREFERENCE;
++            } else {
++                ssl_op_disable |= SSL_OP_CIPHER_SERVER_PREFERENCE;
++                ssl_op_enable &= ~SSL_OP_CIPHER_SERVER_PREFERENCE;
++            }
+         } else if(!regexec(&Ciphers, lin, 4, matches, 0)) {
+             has_other = 1;
+             if(res->ctx == NULL)
+@@ -1105,12 +1133,15 @@ parse_HTTPS(void)
+                 conf_err("ListenHTTPS: can't set SNI callback");
+ #endif
+             for(pc = res->ctx; pc; pc = pc->next) {
++                SSL_CTX_set_app_data(pc->ctx, res);
+                 SSL_CTX_set_mode(pc->ctx, SSL_MODE_AUTO_RETRY);
+-                SSL_CTX_set_options(pc->ctx, SSL_OP_ALL);
++                SSL_CTX_set_options(pc->ctx, ssl_op_enable);
++                SSL_CTX_clear_options(pc->ctx, ssl_op_disable);
+                 sprintf(lin, "%d-Pound-%ld", getpid(), random());
+                 SSL_CTX_set_session_id_context(pc->ctx, (unsigned char *)lin, strlen(lin));
+                 SSL_CTX_set_tmp_rsa_callback(pc->ctx, RSA_tmp_callback);
+                 SSL_CTX_set_tmp_dh_callback(pc->ctx, DH_tmp_callback);
++                SSL_CTX_set_info_callback(pc->ctx, SSLINFO_callback);
+             }
+             return res;
+         } else {
+@@ -1305,6 +1336,8 @@ config_parse(const int argc, char **const argv)
+     || regcomp(&DynScale, "^[ \t]*DynScale[ \t]+([01])[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
+     || regcomp(&ClientCert, "^[ \t]*ClientCert[ \t]+([0-3])[ \t]+([1-9])[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
+     || regcomp(&AddHeader, "^[ \t]*AddHeader[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
++    || regcomp(&SSLAllowClientRenegotiation, "^[ \t]*SSLAllowClientRenegotiation[ \t]+([012])[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
++    || regcomp(&SSLHonorCipherOrder, "^[ \t]*SSLHonorCipherOrder[ \t]+([01])[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
+     || regcomp(&Ciphers, "^[ \t]*Ciphers[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
+     || regcomp(&CAlist, "^[ \t]*CAlist[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
+     || regcomp(&VerifyList, "^[ \t]*VerifyList[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
+@@ -1463,6 +1496,8 @@ config_parse(const int argc, char **const argv)
+     regfree(&DynScale);
+     regfree(&ClientCert);
+     regfree(&AddHeader);
++    regfree(&SSLAllowClientRenegotiation);
++    regfree(&SSLHonorCipherOrder);
+     regfree(&Ciphers);
+     regfree(&CAlist);
+     regfree(&VerifyList);
+diff --git a/http.c b/http.c
+index bb2ce8b..d420b69 100755
+--- a/http.c
++++ b/http.c
+@@ -246,6 +246,11 @@ copy_chunks(BIO *const cl, BIO *const be, LONG *res_bytes, const int no_write, c
+ 
+ static int  err_to = -1;
+ 
++typedef struct {
++    int timeout;
++    RENEG_STATE *reneg_state;
++} BIO_ARG;
++
+ /*
+  * Time-out for client read/gets
+  * the SSL manual says not to do it, but it works well enough anyway...
+@@ -253,18 +258,32 @@ static int  err_to = -1;
+ static long
+ bio_callback(BIO *const bio, const int cmd, const char *argp, int argi, long argl, long ret)
+ {
++    BIO_ARG *bio_arg;
+     struct pollfd   p;
+     int             to, p_res, p_err;
+ 
+     if(cmd != BIO_CB_READ && cmd != BIO_CB_WRITE)
+         return ret;
+ 
++    //logmsg(LOG_NOTICE, "bio callback");
+     /* a time-out already occured */
+-    if((to = *((int *)BIO_get_callback_arg(bio)) * 1000) < 0) {
++    if((bio_arg = (BIO_ARG*)BIO_get_callback_arg(bio))==NULL) return ret;
++    if((to = bio_arg->timeout * 1000) < 0) {
+         errno = ETIMEDOUT;
+         return -1;
+     }
+ 
++    /* Renegotiations */
++    //logmsg(LOG_NOTICE, "RENEG STATE %d", bio_arg->reneg_state==NULL?-1:*bio_arg->reneg_state);
++    if (bio_arg->reneg_state != NULL && *bio_arg->reneg_state == RENEG_ABORT) {
++        logmsg(LOG_NOTICE, "REJECTING renegotiated session");
++        errno = ECONNABORTED;
++        return -1;
++    }
++
++    //logmsg(LOG_NOTICE, "TO %d", to);
++    if (to == 0) return ret;
++
+     for(;;) {
+         memset(&p, 0, sizeof(p));
+         BIO_get_fd(bio, &p.fd);
+@@ -299,7 +318,7 @@ bio_callback(BIO *const bio, const int cmd, const char *argp, int argi, long arg
+             return -1;
+         case 0:
+             /* timeout - mark the BIO as unusable for the future */
+-            BIO_set_callback_arg(bio, (char *)&err_to);
++            bio_arg->timeout = err_to;
+ #ifdef  EBUG
+             logmsg(LOG_WARNING, "(%lx) CALLBACK timeout poll after %d secs: %s",
+                 pthread_self(), to / 1000, strerror(p_err));
+@@ -503,7 +522,14 @@ do_http(thr_arg *arg)
+     regmatch_t          matches[4];
+     struct linger       l;
+     double              start_req, end_req;
+-
++    RENEG_STATE		reneg_state;
++    BIO_ARG		ba1, ba2;
++
++    reneg_state = RENEG_INIT;
++    ba1.reneg_state =  &reneg_state;
++    ba2.reneg_state = &reneg_state;
++    ba1.timeout = 0;
++    ba2.timeout = 0;
+     from_host = ((thr_arg *)arg)->from_host;
+     memcpy(&from_host_addr, from_host.ai_addr, from_host.ai_addrlen);
+     from_host.ai_addr = (struct sockaddr *)&from_host_addr;
+@@ -512,6 +538,8 @@ do_http(thr_arg *arg)
+     free(((thr_arg *)arg)->from_host.ai_addr);
+     free(arg);
+ 
++    if(lstn->allow_cl_reneg) reneg_state = RENEG_ALLOW;
++
+     n = 1;
+     setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (void *)&n, sizeof(n));
+     l.l_onoff = 1;
+@@ -535,10 +563,11 @@ do_http(thr_arg *arg)
+         close(sock);
+         return;
+     }
+-    if(lstn->to > 0) {
+-        BIO_set_callback_arg(cl, (char *)&lstn->to);
++    //if(lstn->to > 0) {
++        ba1.timeout = lstn->to;
++        BIO_set_callback_arg(cl, (char *)&ba1);
+         BIO_set_callback(cl, bio_callback);
+-    }
++    //}
+ 
+     if(lstn->ctx != NULL) {
+         if((ssl = SSL_new(lstn->ctx->ctx)) == NULL) {
+@@ -547,6 +576,7 @@ do_http(thr_arg *arg)
+             BIO_free_all(cl);
+             return;
+         }
++        SSL_set_app_data(ssl, &reneg_state);
+         SSL_set_bio(ssl, cl, cl);
+         if((bb = BIO_new(BIO_f_ssl())) == NULL) {
+             logmsg(LOG_WARNING, "(%lx) BIO_new(Bio_f_ssl()) failed", pthread_self());
+@@ -848,7 +878,8 @@ do_http(thr_arg *arg)
+             }
+             BIO_set_close(be, BIO_CLOSE);
+             if(backend->to > 0) {
+-                BIO_set_callback_arg(be, (char *)&backend->to);
++                ba2.timeout = backend->to;
++                BIO_set_callback_arg(be, (char *)&ba2);
+                 BIO_set_callback(be, bio_callback);
+             }
+             if(backend->ctx != NULL) {
+diff --git a/pound.8 b/pound.8
+index f878a4d..b95e794 100755
+--- a/pound.8
++++ b/pound.8
+@@ -501,6 +501,19 @@ string in the same format as in OpenSSL
+ and
+ .I SSL_CTX_set_cipher_list(3).
+ .TP
++\fBSSLHonorCipherOrder\fR 0|1
++If this value is 1, the server will broadcast a preference to use \fBCiphers\fR in the
++order supplied in the \fBCiphers\fR directive.  If the value is 0, the server will treat
++the Ciphers list as the list of Ciphers it will accept, but no preference will be
++indicated.  Default value is 0.
++.TP
++\fBSSLAllowClientRenegotiation\fR 0|1|2
++If this value is 0, client initiated renegotiation will be disabled.  This will mitigate
++DoS exploits based on client renegotiation, regardless of the patch status of clients and
++servers related to "Secure renegotiation".  If the value is 1, secure renegotiation is
++supported.  If the value is 2, insecure renegotiation is supported, with unpatched
++clients.  /fBThis can lead to a DoS and a Man in the Middle attack!/fR  Default value is 0.
++.TP
+ \fBCAlist\fR "CAcert_file"
+ Set the list of "trusted" CA's for this server. The CAcert_file is a file containing
+ a sequence of CA certificates (PEM format). The names of the defined CA certificates
+diff --git a/pound.h b/pound.h
+index 114db58..3feb0fd 100755
+--- a/pound.h
++++ b/pound.h
+@@ -404,6 +404,7 @@ typedef struct _listener {
+     int                 rewr_dest;      /* rewrite destination header */
+     int                 disabled;       /* true if the listener is disabled */
+     int                 log_level;      /* log level for this listener */
++    int                 allow_cl_reneg; /* Allow Client SSL Renegotiation */
+     SERVICE             *services;
+     struct _listener    *next;
+ }   LISTENER;
+@@ -419,6 +420,9 @@ typedef struct _thr_arg {
+     struct _thr_arg *next;
+ }   thr_arg;                        /* argument to processing threads: socket, origin */
+ 
++/* Track SSL handshare/renegotiation so we can reject client-renegotiations. */
++typedef enum { RENEG_INIT=0, RENEG_REJECT, RENEG_ALLOW, RENEG_ABORT } RENEG_STATE;
++
+ /* Header types */
+ #define HEADER_ILLEGAL              -1
+ #define HEADER_OTHER                0
+@@ -591,6 +595,11 @@ extern RSA  *RSA_tmp_callback(SSL *, int, int);
+ extern DH   *DH_tmp_callback(SSL *, int, int);
+ 
+ /*
++ * Renegotiation callback
++ */
++extern void SSLINFO_callback(const SSL *s, int where, int rc);
++
++/*
+  * expiration stuff
+  */
+ #ifndef EXPIRE_TO
+diff --git a/svc.c b/svc.c
+index fca3e3b..8c33a10 100755
+--- a/svc.c
++++ b/svc.c
+@@ -1797,3 +1797,34 @@ thr_control(void *arg)
+         close(ctl);
+     }
+ }
++
++void
++SSLINFO_callback(const SSL *ssl, int where, int rc)
++{
++    RENEG_STATE *reneg_state;
++
++    /* Get our thr_arg where we're tracking this connection info */
++    if ((reneg_state = (RENEG_STATE *)SSL_get_app_data(ssl)) == NULL) return;
++
++    /* If we're rejecting renegotiations, move to ABORT if Client Hello is being read. */
++    if ((where & SSL_CB_ACCEPT_LOOP) && *reneg_state == RENEG_REJECT) {
++        int state = SSL_get_state(ssl);
++
++        if (state == SSL3_ST_SR_CLNT_HELLO_A || state == SSL23_ST_SR_CLNT_HELLO_A) {
++           *reneg_state = RENEG_ABORT;
++           logmsg(LOG_WARNING,"rejecting client initiated renegotiation");
++        }
++    }
++    else if (where & SSL_CB_HANDSHAKE_DONE && *reneg_state == RENEG_INIT) {
++       // Reject any followup renegotiations
++       *reneg_state = RENEG_REJECT;
++    }
++
++    //if (where & SSL_CB_HANDSHAKE_START) logmsg(LOG_DEBUG, "handshake start");
++    //else if (where & SSL_CB_HANDSHAKE_DONE) logmsg(LOG_DEBUG, "handshake done");
++    //else if (where & SSL_CB_LOOP) logmsg(LOG_DEBUG, "loop");
++    //else if (where & SSL_CB_READ) logmsg(LOG_DEBUG, "read");
++    //else if (where & SSL_CB_WRITE) logmsg(LOG_DEBUG, "write");
++    //else if (where & SSL_CB_ALERT) logmsg(LOG_DEBUG, "alert");
++}
++
+-- 
+1.9.3
+
diff --git a/0002-CVE-2012-4929-CRIME-Disable-SSL-compression.patch b/0002-CVE-2012-4929-CRIME-Disable-SSL-compression.patch
new file mode 100644
index 0000000..bb938e6
--- /dev/null
+++ b/0002-CVE-2012-4929-CRIME-Disable-SSL-compression.patch
@@ -0,0 +1,88 @@
+From e7824ae0d87d2c6375c4296455e39c6cf402ecbe Mon Sep 17 00:00:00 2001
+From: Joe Gooch <mrwizard at k12system.com>
+Date: Fri, 5 Oct 2012 11:41:48 -0400
+Subject: [PATCH 2/5] CVE-2012-4929 CRIME Disable SSL compression
+
+SSL Compression Disable patch for 2.6f
+
+This patch disables SSL/TLS compression entirely.  There is no config option.
+
+This prevents CRIME attacks against SSL.  Note that HTTP compression is still
+an option.
+
+Test your server at https://www.ssllabs.com/ssldb/
+
+Original patch by Hereward Cooper <coops at fawk.eu>
+Openssl 0.9.8 disabling ideas borrowed from Igor Sysoev's code in nginx.
+
+[Picked from https://github.com/goochjj/pound/commit/0b31a8b293dccf700d90946fe71d62891eaf0aa5]
+---
+ config.c |  9 +++++++++
+ pound.c  | 17 +++++++++++++++++
+ 2 files changed, 26 insertions(+)
+
+diff --git a/config.c b/config.c
+index b066dd8..dfe0ff6 100755
+--- a/config.c
++++ b/config.c
+@@ -293,6 +293,9 @@ parse_be(const int is_emergency)
+             SSL_CTX_set_verify(res->ctx, SSL_VERIFY_NONE, NULL);
+             SSL_CTX_set_mode(res->ctx, SSL_MODE_AUTO_RETRY);
+             SSL_CTX_set_options(res->ctx, SSL_OP_ALL);
++#ifdef SSL_OP_NO_COMPRESSION
++            SSL_CTX_set_options(res->ctx, SSL_OP_NO_COMPRESSION);
++#endif
+             SSL_CTX_clear_options(res->ctx, SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION);
+             SSL_CTX_clear_options(res->ctx, SSL_OP_LEGACY_SERVER_CONNECT);
+             sprintf(lin, "%d-Pound-%ld", getpid(), random());
+@@ -313,6 +316,9 @@ parse_be(const int is_emergency)
+             SSL_CTX_set_verify(res->ctx, SSL_VERIFY_NONE, NULL);
+             SSL_CTX_set_mode(res->ctx, SSL_MODE_AUTO_RETRY);
+             SSL_CTX_set_options(res->ctx, SSL_OP_ALL);
++#ifdef SSL_OP_NO_COMPRESSION
++            SSL_CTX_set_options(res->ctx, SSL_OP_NO_COMPRESSION);
++#endif
+             SSL_CTX_clear_options(res->ctx, SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION);
+             SSL_CTX_clear_options(res->ctx, SSL_OP_LEGACY_SERVER_CONNECT);
+             sprintf(lin, "%d-Pound-%ld", getpid(), random());
+@@ -842,6 +848,9 @@ parse_HTTPS(void)
+     POUND_CTX   *pc;
+ 
+     ssl_op_enable = SSL_OP_ALL;
++#ifdef SSL_OP_NO_COMPRESSION
++    ssl_op_enable |= SSL_OP_NO_COMPRESSION;
++#endif
+     ssl_op_disable = SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION | SSL_OP_LEGACY_SERVER_CONNECT;
+ 
+     if((res = (LISTENER *)malloc(sizeof(LISTENER))) == NULL)
+diff --git a/pound.c b/pound.c
+index 9667dcd..79007f6 100755
+--- a/pound.c
++++ b/pound.c
+@@ -251,6 +251,23 @@ main(const int argc, char **argv)
+     CRYPTO_set_locking_callback(l_lock);
+     init_timer();
+ 
++    /* Disable SSL Compression for OpenSSL pre-1.0.  1.0 is handled with an option in config.c */
++#if OPENSSL_VERSION_NUMBER >= 0x00907000L
++#ifndef SSL_OP_NO_COMPRESSION
++    {
++      int i,n;
++      STACK_OF(SSL_COMP) *ssl_comp_methods;
++
++      ssl_comp_methods = SSL_COMP_get_compression_methods();
++      n = sk_SSL_COMP_num(ssl_comp_methods);
++
++      for(i=n-1; i>=0; i--) {
++        sk_SSL_COMP_delete(ssl_comp_methods, i);
++      }
++    }
++#endif
++#endif
++
+     /* prepare regular expressions */
+     if(regcomp(&HEADER, "^([a-z0-9!#$%&'*+.^_`|~-]+):[ \t]*(.*)[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
+     || regcomp(&CHUNK_HEAD, "^([0-9a-f]+).*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
+-- 
+1.9.3
+
diff --git a/0003-Fix-a-XSS-with-a-crafted-URL.patch b/0003-Fix-a-XSS-with-a-crafted-URL.patch
new file mode 100644
index 0000000..3b78f96
--- /dev/null
+++ b/0003-Fix-a-XSS-with-a-crafted-URL.patch
@@ -0,0 +1,80 @@
+From 81caf16a90e63eafbb23cebbe78a5e6df9781196 Mon Sep 17 00:00:00 2001
+From: Joe Gooch <mrwizard at k12system.com>
+Date: Tue, 29 Nov 2011 13:23:32 -0500
+Subject: [PATCH 3/5] Fix a XSS with a crafted URL
+
+[Picked from https://github.com/goochjj/pound/commit/e7124337e3a766985595c0459c1c3ae9d53e9693]
+---
+ http.c | 41 ++++++++++++++++++++++++++++++++++-------
+ 1 file changed, 34 insertions(+), 7 deletions(-)
+
+diff --git a/http.c b/http.c
+index d420b69..10294cf 100755
+--- a/http.c
++++ b/http.c
+@@ -46,13 +46,24 @@ err_reply(BIO *const c, const char *head, const char *txt)
+     return;
+ }
+ 
++
++static char *redir_pre = "<html><head><title>Redirect</title></head><body><h1>Redirect</h1><p>You should go to <a href=\"";
++static char *redir_post = "\">here</a></p></body></html>";
++
++static char hexchar(char a) {
++    a = a & 0xF;
++    if (a > 9) return (a+'a'-10);
++    return a+'0';
++}
++
+ /*
+  * Reply with a redirect
+  */
+ static void
+ redirect_reply(BIO *const c, const char *url, const int code)
+ {
+-    char    rep[MAXBUF], cont[MAXBUF], *code_msg;
++    char    rep[MAXBUF], urlbuf[MAXBUF], ch, *code_msg;
++    int     i,j;
+ 
+     switch(code) {
+     case 301:
+@@ -65,14 +76,30 @@ redirect_reply(BIO *const c, const char *url, const int code)
+         code_msg = "Found";
+         break;
+     }
+-    snprintf(cont, sizeof(cont),
+-        "<html><head><title>Redirect</title></head><body><h1>Redirect</h1><p>You should go to <a href=\"%s\">%s</a></p></body></html>",
+-        url, url);
++    for(i=0,j=0; url[i] && j<MAXBUF-1; i++) {
++        ch = url[i];
++        if (
++            (ch>= 'A' && ch <='Z') ||
++            (ch>= 'a' && ch <='z') ||
++            (ch>= '0' && ch <='9') ||
++            ch == '-' || ch == '_' || ch == '.' || ch == ':' || ch == '/' || ch == '?' || ch == '&' || ch == ';') {
++
++            urlbuf[j++] = ch;
++            continue;
++        }
++        if (j>MAXBUF-4) break;
++        urlbuf[j++] = '%';
++        urlbuf[j++] = hexchar(ch>>4);
++        urlbuf[j++] = hexchar(ch);
++    }
++    urlbuf[j++] = 0;
+     snprintf(rep, sizeof(rep),
+-        "HTTP/1.0 %d %s\r\nLocation: %s\r\nContent-Type: text/html\r\nContent-Length: %d\r\n\r\n",
+-        code, code_msg, url, strlen(cont));
++        "HTTP/1.0 %d %s\r\nLocation: %s\r\nContent-Type: text/html\r\nContent-Length: %u\r\n\r\n",
++        code, code_msg, urlbuf, (unsigned int)(strlen(redir_pre)+strlen(redir_post)+strlen(urlbuf)));
+     BIO_write(c, rep, strlen(rep));
+-    BIO_write(c, cont, strlen(cont));
++    BIO_write(c, redir_pre, strlen(redir_pre));
++    BIO_write(c, urlbuf, strlen(urlbuf));
++    BIO_write(c, redir_post, strlen(redir_post));
+     BIO_flush(c);
+     return;
+ }
+-- 
+1.9.3
+
diff --git a/0004-CVE-2005-2090-chunked-encoding-response-splitting.patch b/0004-CVE-2005-2090-chunked-encoding-response-splitting.patch
new file mode 100644
index 0000000..07ab10f
--- /dev/null
+++ b/0004-CVE-2005-2090-chunked-encoding-response-splitting.patch
@@ -0,0 +1,81 @@
+From 31e6ab156d4d43fc3180b20a949b9255c493cb4f Mon Sep 17 00:00:00 2001
+From: Joe Gooch <mrwizard at k12system.com>
+Date: Fri, 24 Oct 2014 10:18:42 -0400
+Subject: [PATCH 4/5] CVE-2005-2090 chunked encoding response-splitting
+
+Apply David Martineau's CVE-2005-2090 patch
+
+[Picked from https://github.com/goochjj/pound/commit/4b324d753d80aaa3ccda74a6d9094b456fc2d4e4]
+---
+ http.c | 29 +++++++++++++++++++++++++----
+ 1 file changed, 25 insertions(+), 4 deletions(-)
+
+diff --git a/http.c b/http.c
+index 10294cf..57869cd 100755
+--- a/http.c
++++ b/http.c
+@@ -533,7 +533,7 @@ log_bytes(char *res, const LONG cnt)
+ void
+ do_http(thr_arg *arg)
+ {
+-    int                 cl_11, be_11, res, chunked, n, sock, no_cont, skip, conn_closed, force_10, sock_proto, is_rpc;
++    int                 cl_count,cl_11, be_11, res, chunked, n, sock, no_cont, skip, conn_closed, force_10, sock_proto, is_rpc;
+     LISTENER            *lstn;
+     SERVICE             *svc;
+     BACKEND             *backend, *cur_backend, *old_backend;
+@@ -708,7 +708,7 @@ do_http(thr_arg *arg)
+         }
+ 
+         /* check other headers */
+-        for(chunked = 0, cont = L_1, n = 1; n < MAXHEADERS && headers[n]; n++) {
++        for(cl_count=0,chunked = 0, cont = L_1, n = 1; n < MAXHEADERS && headers[n]; n++) {
+             /* no overflow - see check_header for details */
+             switch(check_header(headers[n], buf)) {
+             case HEADER_HOST:
+@@ -734,11 +734,28 @@ do_http(thr_arg *arg)
+                         chunked = 1;
+                 break;
+             case HEADER_CONTENT_LENGTH:
++                cl_count++;
++                if (cl_count>1)
++                {
++                    logmsg(LOG_WARNING, "(%lx) e501 bad multi-content-length request \"%s\" from %s", pthread_self(), request, caddr);
++                    err_reply(cl, h501, lstn->err501);
++                    free_headers(headers);
++                    clean_all();
++                    return;
++                }
++
+                 if(chunked || cont >= 0L)
++                {
+                     headers_ok[n] = 0;
+-                else
++                }
++                else {
+                     if((cont = ATOL(buf)) < 0L)
++                     {
+                         headers_ok[n] = 0;
++                     }
++                    if(is_rpc == 1 && (cont < 0x20000L || cont > 0x80000000L))
++                        is_rpc = -1;
++                }
+                 break;
+             case HEADER_ILLEGAL:
+                 if(lstn->log_level > 0) {
+@@ -1397,8 +1414,12 @@ do_http(thr_arg *arg)
+                 case HEADER_CONTENT_LENGTH:
+                     cont = ATOL(buf);
+                     /* treat RPC_OUT_DATA like reply without content-length */
+-                    if(is_rpc == 0 && cont == 0x40000000L)
++                    if(is_rpc == 0) {
++                        if(cont >= 0x20000L && cont <= 0x80000000L)
+                         cont = -1L;
++                        else
++                            is_rpc = -1;
++                    }
+                     break;
+                 case HEADER_LOCATION:
+                     if(v_host[0] && need_rewrite(lstn->rewr_loc, buf, loc_path, v_host, lstn, cur_backend)) {
+-- 
+1.9.3
+
diff --git a/0005-CVE-2014-3566-POODLE-Backport-Disable-proto-option.patch b/0005-CVE-2014-3566-POODLE-Backport-Disable-proto-option.patch
new file mode 100644
index 0000000..c26d584
--- /dev/null
+++ b/0005-CVE-2014-3566-POODLE-Backport-Disable-proto-option.patch
@@ -0,0 +1,86 @@
+From 2b61e146df9eda03a7fe34c884de261cc18d36ff Mon Sep 17 00:00:00 2001
+From: Lubomir Rintel <lkundrak at v3.sk>
+Date: Fri, 24 Oct 2014 17:07:15 +0200
+Subject: [PATCH 5/5] CVE-2014-3566 POODLE Backport "Disable <proto>" option
+
+This allows removing support for vulnerable SSL versions.
+---
+ config.c | 22 +++++++++++++++++++++-
+ pound.8  |  7 +++++++
+ 2 files changed, 28 insertions(+), 1 deletion(-)
+
+diff --git a/config.c b/config.c
+index dfe0ff6..78e3cf7 100755
+--- a/config.c
++++ b/config.c
+@@ -76,7 +76,7 @@ static regex_t  ListenHTTP, ListenHTTPS, End, Address, Port, Cert, xHTTP, Client
+ static regex_t  Err414, Err500, Err501, Err503, MaxRequest, HeadRemove, RewriteLocation, RewriteDestination;
+ static regex_t  Service, ServiceName, URL, HeadRequire, HeadDeny, BackEnd, Emergency, Priority, HAport, HAportAddr;
+ static regex_t  Redirect, RedirectN, TimeOut, Session, Type, TTL, ID, DynScale;
+-static regex_t  ClientCert, AddHeader, SSLAllowClientRenegotiation, SSLHonorCipherOrder, Ciphers, CAlist, VerifyList, CRLlist, NoHTTPS11;
++static regex_t  ClientCert, AddHeader, DisableProto, SSLAllowClientRenegotiation, SSLHonorCipherOrder, Ciphers, CAlist, VerifyList, CRLlist, NoHTTPS11;
+ static regex_t  Grace, Include, ConnTO, IgnoreCase, HTTPS, HTTPSCert, Disabled, Threads, CNName;
+ 
+ static regmatch_t   matches[5];
+@@ -1049,6 +1049,24 @@ parse_HTTPS(void)
+                 strcat(res->add_head, "\r\n");
+                 strcat(res->add_head, lin + matches[1].rm_so);
+             }
++        } else if(!regexec(&DisableProto, lin, 4, matches, 0)) {
++            lin[matches[1].rm_eo] = '\0';
++            if(strcasecmp(lin + matches[1].rm_so, "SSLv2") == 0)
++                ssl_op_enable |= SSL_OP_NO_SSLv2;
++            else if(strcasecmp(lin + matches[1].rm_so, "SSLv3") == 0)
++                ssl_op_enable |= SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3;
++#ifdef SSL_OP_NO_TLSv1
++            else if(strcasecmp(lin + matches[1].rm_so, "TLSv1") == 0)
++                ssl_op_enable |= SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1;
++#endif
++#ifdef SSL_OP_NO_TLSv1_1
++            else if(strcasecmp(lin + matches[1].rm_so, "TLSv1_1") == 0)
++                ssl_op_enable |= SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1;
++#endif
++#ifdef SSL_OP_NO_TLSv1_2
++            else if(strcasecmp(lin + matches[1].rm_so, "TLSv1_2") == 0)
++                ssl_op_enable |= SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1 | SSL_OP_NO_TLSv1_2;
++#endif
+         } else if(!regexec(&SSLAllowClientRenegotiation, lin, 4, matches, 0)) {
+             res->allow_cl_reneg = atoi(lin + matches[1].rm_so);
+             if (res->allow_cl_reneg == 2) {
+@@ -1346,6 +1364,7 @@ config_parse(const int argc, char **const argv)
+     || regcomp(&ClientCert, "^[ \t]*ClientCert[ \t]+([0-3])[ \t]+([1-9])[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
+     || regcomp(&AddHeader, "^[ \t]*AddHeader[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
+     || regcomp(&SSLAllowClientRenegotiation, "^[ \t]*SSLAllowClientRenegotiation[ \t]+([012])[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
++    || regcomp(&DisableProto, "^[ \t]*Disable[ \t](SSLv2|SSLv3|TLSv1|TLSv1_1|TLSv1_2)[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
+     || regcomp(&SSLHonorCipherOrder, "^[ \t]*SSLHonorCipherOrder[ \t]+([01])[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
+     || regcomp(&Ciphers, "^[ \t]*Ciphers[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
+     || regcomp(&CAlist, "^[ \t]*CAlist[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
+@@ -1506,6 +1525,7 @@ config_parse(const int argc, char **const argv)
+     regfree(&ClientCert);
+     regfree(&AddHeader);
+     regfree(&SSLAllowClientRenegotiation);
++    regfree(&DisableProto);
+     regfree(&SSLHonorCipherOrder);
+     regfree(&Ciphers);
+     regfree(&CAlist);
+diff --git a/pound.8 b/pound.8
+index b95e794..8bb2bd0 100755
+--- a/pound.8
++++ b/pound.8
+@@ -494,6 +494,13 @@ is the depth of verification for a client certificate (up to 9). The default
+ depth limit is 9, allowing for the peer certificate and additional 9 CA
+ certificates that must be verified.
+ .TP
++\fBDisable\fR SSLv2|SSLv3|TLSv1|TLSv1_1|TLSv1_2
++Disable the protocol \fBand all lower protocols as well\fR.
++This is due to a limitation in OpenSSL, which does not support disabling a single
++protocol. For example,
++.I Disable TLSv1
++would disable SSLv2, SSLv3 and TLSv1, thus allowing only TLSv1_1 and TLSv1_2.
++.TP
+ \fBCiphers\fR "acceptable:cipher:list"
+ This is the list of ciphers that will be accepted by the SSL connection; it is a
+ string in the same format as in OpenSSL
+-- 
+1.9.3
+
diff --git a/Pound.spec b/Pound.spec
index 9df819b..c964a54 100644
--- a/Pound.spec
+++ b/Pound.spec
@@ -4,7 +4,7 @@
 
 Name:        Pound
 Version:     2.6
-Release:     7%{?dist}
+Release:     8%{?dist}
 Summary:     Reverse proxy and load balancer
 Group:       System Environment/Daemons
 License:     GPLv3
@@ -32,6 +32,22 @@ Source1:  pound.service
 Source2:  pound.cfg
 Patch0:   pound-remove-owner.patch
 
+# To the dearest soul that decides to keep this up to date with security
+# developments:
+#
+# Security backports. Upstream doesn't do these and their mailing list
+# is essentially a place where users exchange patches without upstream
+# oversight. This is a branch where interesting fixes get applied to
+# stable release, sadly with poor coordination in upstream, often adding
+# different configuration mechanisms than upstream does, wrecking the
+# upgrade path: https://github.com/goochjj/pound/commits/pcidss/v2.6
+# Be careful when picking from there; prefer to backport from alphas.
+Patch1:   0001-CVE-2011-3389-BEAST-Add-options-preventing-users-fro.patch
+Patch2:   0002-CVE-2012-4929-CRIME-Disable-SSL-compression.patch
+Patch3:   0003-Fix-a-XSS-with-a-crafted-URL.patch
+Patch4:   0004-CVE-2005-2090-chunked-encoding-response-splitting.patch
+Patch5:   0005-CVE-2014-3566-POODLE-Backport-Disable-proto-option.patch
+
 %description
 The Pound program is a reverse proxy, load balancer and
 HTTPS front-end for Web server(s). Pound was developed
@@ -44,6 +60,11 @@ give away
 %prep
 %setup -q
 %patch0 -p1 -b .remove-owner
+%patch1 -p1
+%patch2 -p1
+%patch3 -p1
+%patch4 -p1
+%patch5 -p1
 
 %build
 %configure
@@ -115,6 +136,15 @@ exit 0
 %attr(-,%{pound_user},%{pound_group}) %dir %{pound_home}
 
 %changelog
+* Fri Oct 24 2014 Lubomir Rintel <lkundrak at v3.sk> - 2.6-8
+- Backport various security fixes. Note they usually are extra options that need
+  to be enabled manually so that we won't break functionality:
+- CVE-2011-3389: Make it possible to deny use of "BEAST" vulnerable ciphers
+- CVE-2012-4929: Disable compression to be safe from "CRIME"
+- CVE-2005-2090: Chunked encofing response splitting (no awkward name here)
+- CVE-2014-3566: Allow disabling SSLv3 (and others), to be safe from "POODLE"
+- A redirect XSS fix
+
 * Fri Aug 02 2013 Fedora Release Engineering <rel-eng at lists.fedoraproject.org> - 2.6-7
 - Rebuilt for https://fedoraproject.org/wiki/Fedora_20_Mass_Rebuild
 


More information about the scm-commits mailing list