]> git.ipfire.org Git - thirdparty/apache/httpd.git/commitdiff
mod_ssl: Switch to using SSL_OP_NO_RENEGOTATION (where available) to
authorJoe Orton <jorton@apache.org>
Tue, 5 May 2020 12:40:38 +0000 (12:40 +0000)
committerJoe Orton <jorton@apache.org>
Tue, 5 May 2020 12:40:38 +0000 (12:40 +0000)
block client-initiated renegotiation with TLSv1.2 and earlier.

* modules/ssl/ssl_private.h: Define modssl_reneg_state enum,
  modssl_set_reneg_state function.

* modules/ssl/ssl_engine_io.c (bio_filter_out_write,
  bio_filter_in_read): #ifdef-out reneg protection if
  SSL_OP_NO_RENEGOTATION is defined.

* modules/ssl/ssl_engine_init.c (ssl_init_ctx_protocol):
  Enable SSL_OP_NO_RENEGOTATION.
  (ssl_init_ctx_callbacks): Only enable the "info" callback if
  debug-level logging *or* OpenSSL doesn't support SSL_OP_NO_RENEGOTATION.

* modules/ssl/ssl_engine_kernel.c (ssl_hook_Access_classic): Use
  modssl_set_reneg_state to set the reneg protection mode.
  (ssl_hook_Access_modern): Drop manipulation of the reneg mode which
  does nothing for TLSv1.3 already.
  (ssl_callback_Info): Only enable reneg protection if
  SSL_OP_NO_RENEGOTATION is *not* defined.

* modules/ssl/ssl_util_ssl.c (modssl_set_reneg_state): New function.

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1877397 13f79535-47bb-0310-9956-ffa450edef68

CHANGES
modules/ssl/ssl_engine_init.c
modules/ssl/ssl_engine_io.c
modules/ssl/ssl_engine_kernel.c
modules/ssl/ssl_private.h
modules/ssl/ssl_util_ssl.c

diff --git a/CHANGES b/CHANGES
index f6aae5ee536a47d0606c086990287b8e8e054528..0e1b976bf55cafa7bb1367814923bd9bb7b578bd 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,6 +1,11 @@
                                                          -*- coding: utf-8 -*-
 Changes with Apache 2.5.1
 
+  *) mod_ssl: With OpenSSL 1.1.1 and later, client-initiated
+     renegotiation in TLSv1.2 and earlier is blocked at SSL library
+     level (with a TLS warning alert sent), rather than by aborting
+     the connection inside mod_ssl.  [Joe Orton]
   *) core: Add optional "options=" argument to Listen.  Supported
      keywords are "freebind" and "reuseport".  PR 61865.
      [Jan Kaluza, Lubos Uhliarik <luhliari redhat.com>, Joe Orton]
index 3ebad301d26c93e1182ce32cb91cf870722ebb63..129d09824a37499d7281d1856bd93f7d8a897a53 100644 (file)
@@ -858,6 +858,13 @@ static apr_status_t ssl_init_ctx_protocol(server_rec *s,
         SSL_CTX_set_keylog_callback(ctx, modssl_callback_keylog);
     }
 #endif
+
+#ifdef SSL_OP_NO_RENEGOTIATION
+    /* For server-side SSL_CTX, disable renegotiation by default.. */
+    if (!mctx->pkp) {
+        SSL_CTX_set_options(ctx, SSL_OP_NO_RENEGOTIATION);
+    }
+#endif
     
     return APR_SUCCESS;
 }
@@ -879,6 +886,14 @@ static void ssl_init_ctx_session_cache(server_rec *s,
     }
 }
 
+#ifdef SSL_OP_NO_RENEGOTIATION
+/* OpenSSL-level renegotiation protection. */
+#define MODSSL_BLOCKS_RENEG (0)
+#else
+/* mod_ssl-level renegotiation protection. */
+#define MODSSL_BLOCKS_RENEG (1)
+#endif
+
 static void ssl_init_ctx_callbacks(server_rec *s,
                                    apr_pool_t *p,
                                    apr_pool_t *ptemp,
@@ -888,7 +903,13 @@ static void ssl_init_ctx_callbacks(server_rec *s,
 
     SSL_CTX_set_tmp_dh_callback(ctx,  ssl_callback_TmpDH);
 
-    SSL_CTX_set_info_callback(ctx, ssl_callback_Info);
+    /* The info callback is used for debug-level tracing.  For OpenSSL
+     * versions where SSL_OP_NO_RENEGOTIATION is not available, the
+     * callback is also used to prevent use of client-initiated
+     * renegotiation.  Enable it in either case. */
+    if (APLOGdebug(s) || MODSSL_BLOCKS_RENEG) {
+        SSL_CTX_set_info_callback(ctx, ssl_callback_Info);
+    }
 
 #ifdef HAVE_TLS_ALPN
     SSL_CTX_set_alpn_select_cb(ctx, ssl_callback_alpn_select, NULL);
index 771d29c675645a5f71e959b01698f414dddac41f..8e44413031db3e2b6794cefa21e6212da0380f0c 100644 (file)
@@ -211,11 +211,13 @@ static int bio_filter_out_write(BIO *bio, const char *in, int inl)
 
     BIO_clear_retry_flags(bio);
 
+#ifndef SSL_OP_NO_RENEGOTIATION
     /* Abort early if the client has initiated a renegotiation. */
     if (outctx->filter_ctx->config->reneg_state == RENEG_ABORT) {
         outctx->rc = APR_ECONNABORTED;
         return -1;
     }
+#endif
 
     ap_log_cerror(APLOG_MARK, APLOG_TRACE6, 0, outctx->c,
                   "bio_filter_out_write: %i bytes", inl);
@@ -514,11 +516,13 @@ static int bio_filter_in_read(BIO *bio, char *in, int inlen)
 
     BIO_clear_retry_flags(bio);
 
+#ifndef SSL_OP_NO_RENEGOTIATION
     /* Abort early if the client has initiated a renegotiation. */
     if (inctx->filter_ctx->config->reneg_state == RENEG_ABORT) {
         inctx->rc = APR_ECONNABORTED;
         return -1;
     }
+#endif
 
     if (!inctx->bb) {
         inctx->rc = APR_EOF;
index 7c1811ea17fd78bc80a2cf29d4d940858e41ca95..81a4513d67479714dc8bff3c2bc3b9448e7d1f6c 100644 (file)
@@ -992,7 +992,7 @@ static int ssl_hook_Access_classic(request_rec *r, SSLSrvConfigRec *sc, SSLDirCo
 
             /* Toggle the renegotiation state to allow the new
              * handshake to proceed. */
-            sslconn->reneg_state = RENEG_ALLOW;
+            modssl_set_reneg_state(sslconn, RENEG_ALLOW);
 
             SSL_renegotiate(ssl);
             SSL_do_handshake(ssl);
@@ -1019,7 +1019,7 @@ static int ssl_hook_Access_classic(request_rec *r, SSLSrvConfigRec *sc, SSLDirCo
              */
             SSL_peek(ssl, peekbuf, 0);
 
-            sslconn->reneg_state = RENEG_REJECT;
+            modssl_set_reneg_state(sslconn, RENEG_REJECT);
 
             if (!SSL_is_init_finished(ssl)) {
                 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02261)
@@ -1078,7 +1078,7 @@ static int ssl_hook_Access_modern(request_rec *r, SSLSrvConfigRec *sc, SSLDirCon
         (sc->server->auth.verify_mode != SSL_CVERIFY_UNSET)) {
         int vmode_inplace, vmode_needed;
         int change_vmode = FALSE;
-        int old_state, n, rc;
+        int n, rc;
 
         vmode_inplace = SSL_get_verify_mode(ssl);
         vmode_needed = SSL_VERIFY_NONE;
@@ -1182,8 +1182,6 @@ static int ssl_hook_Access_modern(request_rec *r, SSLSrvConfigRec *sc, SSLDirCon
                 return HTTP_FORBIDDEN;
             }
             
-            old_state = sslconn->reneg_state;
-            sslconn->reneg_state = RENEG_ALLOW;
             modssl_set_app_data2(ssl, r);
 
             SSL_do_handshake(ssl);
@@ -1193,7 +1191,6 @@ static int ssl_hook_Access_modern(request_rec *r, SSLSrvConfigRec *sc, SSLDirCon
              */
             SSL_peek(ssl, peekbuf, 0);
 
-            sslconn->reneg_state = old_state;
             modssl_set_app_data2(ssl, NULL);
 
             /*
@@ -2267,8 +2264,8 @@ static void log_tracing_state(const SSL *ssl, conn_rec *c,
 /*
  * This callback function is executed while OpenSSL processes the SSL
  * handshake and does SSL record layer stuff.  It's used to trap
- * client-initiated renegotiations, and for dumping everything to the
- * log.
+ * client-initiated renegotiations (where SSL_OP_NO_RENEGOTATION is
+ * not available), and for dumping everything to the log.
  */
 void ssl_callback_Info(const SSL *ssl, int where, int rc)
 {
@@ -2280,14 +2277,12 @@ void ssl_callback_Info(const SSL *ssl, int where, int rc)
         return;
     }
 
-    /* With TLS 1.3 this callback may be called multiple times on the first
-     * negotiation, so the below logic to detect renegotiations can't work.
-     * Fortunately renegotiations are forbidden starting with TLS 1.3, and
-     * this is enforced by OpenSSL so there's nothing to be done here.
-     */
-#if SSL_HAVE_PROTOCOL_TLSV1_3
-    if (SSL_version(ssl) < TLS1_3_VERSION)
-#endif
+#ifndef SSL_OP_NO_RENEGOTATION
+    /* With OpenSSL < 1.1.1 (implying TLS v1.2 or earlier), this
+     * callback is used to block client-initiated renegotiation.  With
+     * TLSv1.3 it is unnecessary since renegotiation is forbidden at
+     * protocol level.  Otherwise (TLSv1.2 with OpenSSL >=1.1.1),
+     * SSL_OP_NO_RENEGOTATION is used to block renegotiation. */
     {
         SSLConnRec *sslconn;
 
@@ -2312,6 +2307,7 @@ void ssl_callback_Info(const SSL *ssl, int where, int rc)
             sslconn->reneg_state = RENEG_REJECT;
         }
     }
+#endif
 
     s = mySrvFromConn(c);
     if (s && APLOGdebug(s)) {
index e5c8c0ccc5f4241ba0c9410bdcf134aac06da2c4..a4b88a531df6a7ed0c6d4edc847c4f7c772df603 100644 (file)
@@ -500,6 +500,16 @@ typedef struct {
     apr_time_t     source_mtime;
 } ssl_asn1_t;
 
+typedef enum {
+    RENEG_INIT = 0, /* Before initial handshake */
+    RENEG_REJECT,   /* After initial handshake; any client-initiated
+                     * renegotiation should be rejected */
+    RENEG_ALLOW,    /* A server-initiated renegotiation is taking
+                     * place (as dictated by configuration) */
+    RENEG_ABORT     /* Renegotiation initiated by client, abort the
+                     * connection */
+} modssl_reneg_state;
+
 /**
  * Define the mod_ssl per-module configuration structure
  * (i.e. the global configuration for each httpd process)
@@ -532,18 +542,13 @@ typedef struct {
         NON_SSL_SET_ERROR_MSG  /* Need to set the error message */
     } non_ssl_request;
 
-    /* Track the handshake/renegotiation state for the connection so
-     * that all client-initiated renegotiations can be rejected, as a
-     * partial fix for CVE-2009-3555. */
-    enum {
-        RENEG_INIT = 0, /* Before initial handshake */
-        RENEG_REJECT,   /* After initial handshake; any client-initiated
-                         * renegotiation should be rejected */
-        RENEG_ALLOW,    /* A server-initiated renegotiation is taking
-                         * place (as dictated by configuration) */
-        RENEG_ABORT     /* Renegotiation initiated by client, abort the
-                         * connection */
-    } reneg_state;
+#ifndef SSL_OP_NO_RENEGOTATION
+    /* For OpenSSL < 1.1.1, track the handshake/renegotiation state
+     * for the connection to block client-initiated renegotiations.
+     * For OpenSSL >=1.1.1, the SSL_OP_NO_RENEGOTATION flag is used in
+     * the SSL * options state with equivalent effect. */
+    modssl_reneg_state reneg_state;
+#endif
 
     server_rec *server;
     SSLDirConfigRec *dc;
@@ -1146,6 +1151,9 @@ extern int ssl_running_on_valgrind;
 int ssl_is_challenge(conn_rec *c, const char *servername, 
                      X509 **pcert, EVP_PKEY **pkey);
 
+/* Set the renegotation state for connection. */
+void modssl_set_reneg_state(SSLConnRec *sslconn, modssl_reneg_state state);
+
 #endif /* SSL_PRIVATE_H */
 /** @} */
 
index 74088f5e298e83f58204087f52a818764ca23507..ef18f5e1f56ca9b7ea5c19d70e6bdcc73710adb2 100644 (file)
@@ -511,3 +511,19 @@ char *modssl_SSL_SESSION_id2sz(IDCONST unsigned char *id, int idlen,
 
     return str;
 }
+
+void modssl_set_reneg_state(SSLConnRec *sslconn, modssl_reneg_state state)
+{
+#ifdef SSL_OP_NO_RENEGOTATION
+    switch (state) {
+    case RENEG_ALLOW:
+        SSL_clear_options(sslconn->ssl, SSL_OP_NO_RENEGOTATION);
+        break;
+    default:
+        SSL_set_options(sslconn->ssl, SSL_OP_NO_RENEGOTATION);
+        break;
+    }
+#else
+    sslconn->reneg_state = state;
+#endif
+}