From: Joe Gooch <mrwizard@k12system.com>
Author: Joe Gooch <mrwizard@k12system.com>
Description: SSL Renegotiation Patch
 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/
Forwarded: http://www.apsis.ch/pound/pound_list/archive/2012/2012-02/1328105174000#1328207465000
--- a/config.c
+++ b/config.c
@@ -76,7 +76,7 @@
 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 @@
         } 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 @@
         } 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 @@
             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 @@
     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 @@
     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_client_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 @@
                 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_client_reneg = atoi(lin + matches[1].rm_so);
+            if (res->allow_client_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 @@
                 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 @@
     || 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 @@
     regfree(&DynScale);
     regfree(&ClientCert);
     regfree(&AddHeader);
+    regfree(&SSLAllowClientRenegotiation);
+    regfree(&SSLHonorCipherOrder);
     regfree(&Ciphers);
     regfree(&CAlist);
     regfree(&VerifyList);
--- a/pound.8
+++ b/pound.8
@@ -501,6 +501,19 @@
 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
--- a/pound.h
+++ b/pound.h
@@ -404,6 +404,7 @@
     int                 rewr_dest;      /* rewrite destination header */
     int                 disabled;       /* true if the listener is disabled */
     int                 log_level;      /* log level for this listener */
+    int                 allow_client_reneg; /* Allow Client SSL Renegotiation */
     SERVICE             *services;
     struct _listener    *next;
 }   LISTENER;
@@ -419,6 +420,9 @@
     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 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
--- a/svc.c
+++ b/svc.c
@@ -1797,3 +1797,34 @@
         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");
+}
+

