https://github.com/apache/httpd/pull/426.patch Upstream-Status: in trunk, proposed for 2.4.60 --- httpd-2.4.59/modules/ssl/ssl_engine_init.c.mr426 +++ httpd-2.4.59/modules/ssl/ssl_engine_init.c @@ -880,6 +880,13 @@ } #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 + #ifdef SSL_OP_IGNORE_UNEXPECTED_EOF /* For server-side SSL_CTX, enable ignoring unexpected EOF */ /* (OpenSSL 1.1.1 behavioural compatibility).. */ @@ -908,6 +915,14 @@ } } +#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, @@ -921,7 +936,13 @@ SSL_CTX_set_tmp_dh_callback(ctx, ssl_callback_TmpDH); #endif - 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); --- httpd-2.4.59/modules/ssl/ssl_engine_io.c.mr426 +++ httpd-2.4.59/modules/ssl/ssl_engine_io.c @@ -208,11 +208,13 @@ 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); @@ -473,11 +475,13 @@ 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; --- httpd-2.4.59/modules/ssl/ssl_engine_kernel.c.mr426 +++ httpd-2.4.59/modules/ssl/ssl_engine_kernel.c @@ -992,7 +992,7 @@ /* 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 @@ */ 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 @@ (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; @@ -1180,8 +1180,6 @@ return HTTP_FORBIDDEN; } - old_state = sslconn->reneg_state; - sslconn->reneg_state = RENEG_ALLOW; modssl_set_app_data2(ssl, r); SSL_do_handshake(ssl); @@ -1191,7 +1189,6 @@ */ SSL_peek(ssl, peekbuf, 0); - sslconn->reneg_state = old_state; modssl_set_app_data2(ssl, NULL); /* @@ -2263,8 +2260,8 @@ /* * 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_RENEGOTIATION is + * not available), and for dumping everything to the log. */ void ssl_callback_Info(const SSL *ssl, int where, int rc) { @@ -2276,14 +2273,12 @@ 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_RENEGOTIATION + /* 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_RENEGOTIATION is used to block renegotiation. */ { SSLConnRec *sslconn; @@ -2308,6 +2303,7 @@ sslconn->reneg_state = RENEG_REJECT; } } +#endif s = mySrvFromConn(c); if (s && APLOGdebug(s)) { --- httpd-2.4.59/modules/ssl/ssl_private.h.mr426 +++ httpd-2.4.59/modules/ssl/ssl_private.h @@ -558,6 +558,16 @@ 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) @@ -589,18 +599,13 @@ 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_RENEGOTIATION + /* 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_RENEGOTIATION flag is used in + * the SSL * options state with equivalent effect. */ + modssl_reneg_state reneg_state; +#endif server_rec *server; SSLDirConfigRec *dc; @@ -1207,6 +1212,9 @@ * the configured ENGINE. */ int modssl_is_engine_id(const char *name); +/* Set the renegotation state for connection. */ +void modssl_set_reneg_state(SSLConnRec *sslconn, modssl_reneg_state state); + #endif /* SSL_PRIVATE_H */ /** @} */ --- httpd-2.4.59/modules/ssl/ssl_util_ssl.c.mr426 +++ httpd-2.4.59/modules/ssl/ssl_util_ssl.c @@ -612,3 +612,19 @@ } return rv; } + +void modssl_set_reneg_state(SSLConnRec *sslconn, modssl_reneg_state state) +{ +#ifdef SSL_OP_NO_RENEGOTIATION + switch (state) { + case RENEG_ALLOW: + SSL_clear_options(sslconn->ssl, SSL_OP_NO_RENEGOTIATION); + break; + default: + SSL_set_options(sslconn->ssl, SSL_OP_NO_RENEGOTIATION); + break; + } +#else + sslconn->reneg_state = state; +#endif +}