mirror of
https://github.com/apache/httpd.git
synced 2025-08-07 04:02:58 +03:00
mod_ssl: Switch to using SSL_OP_NO_RENEGOTATION (where available) to
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
This commit is contained in:
5
CHANGES
5
CHANGES
@@ -1,6 +1,11 @@
|
|||||||
-*- coding: utf-8 -*-
|
-*- coding: utf-8 -*-
|
||||||
Changes with Apache 2.5.1
|
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
|
*) core: Add optional "options=" argument to Listen. Supported
|
||||||
keywords are "freebind" and "reuseport". PR 61865.
|
keywords are "freebind" and "reuseport". PR 61865.
|
||||||
[Jan Kaluza, Lubos Uhliarik <luhliari redhat.com>, Joe Orton]
|
[Jan Kaluza, Lubos Uhliarik <luhliari redhat.com>, Joe Orton]
|
||||||
|
@@ -858,6 +858,13 @@ static apr_status_t ssl_init_ctx_protocol(server_rec *s,
|
|||||||
SSL_CTX_set_keylog_callback(ctx, modssl_callback_keylog);
|
SSL_CTX_set_keylog_callback(ctx, modssl_callback_keylog);
|
||||||
}
|
}
|
||||||
#endif
|
#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;
|
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,
|
static void ssl_init_ctx_callbacks(server_rec *s,
|
||||||
apr_pool_t *p,
|
apr_pool_t *p,
|
||||||
apr_pool_t *ptemp,
|
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_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
|
#ifdef HAVE_TLS_ALPN
|
||||||
SSL_CTX_set_alpn_select_cb(ctx, ssl_callback_alpn_select, NULL);
|
SSL_CTX_set_alpn_select_cb(ctx, ssl_callback_alpn_select, NULL);
|
||||||
|
@@ -211,11 +211,13 @@ static int bio_filter_out_write(BIO *bio, const char *in, int inl)
|
|||||||
|
|
||||||
BIO_clear_retry_flags(bio);
|
BIO_clear_retry_flags(bio);
|
||||||
|
|
||||||
|
#ifndef SSL_OP_NO_RENEGOTIATION
|
||||||
/* Abort early if the client has initiated a renegotiation. */
|
/* Abort early if the client has initiated a renegotiation. */
|
||||||
if (outctx->filter_ctx->config->reneg_state == RENEG_ABORT) {
|
if (outctx->filter_ctx->config->reneg_state == RENEG_ABORT) {
|
||||||
outctx->rc = APR_ECONNABORTED;
|
outctx->rc = APR_ECONNABORTED;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
ap_log_cerror(APLOG_MARK, APLOG_TRACE6, 0, outctx->c,
|
ap_log_cerror(APLOG_MARK, APLOG_TRACE6, 0, outctx->c,
|
||||||
"bio_filter_out_write: %i bytes", inl);
|
"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);
|
BIO_clear_retry_flags(bio);
|
||||||
|
|
||||||
|
#ifndef SSL_OP_NO_RENEGOTIATION
|
||||||
/* Abort early if the client has initiated a renegotiation. */
|
/* Abort early if the client has initiated a renegotiation. */
|
||||||
if (inctx->filter_ctx->config->reneg_state == RENEG_ABORT) {
|
if (inctx->filter_ctx->config->reneg_state == RENEG_ABORT) {
|
||||||
inctx->rc = APR_ECONNABORTED;
|
inctx->rc = APR_ECONNABORTED;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (!inctx->bb) {
|
if (!inctx->bb) {
|
||||||
inctx->rc = APR_EOF;
|
inctx->rc = APR_EOF;
|
||||||
|
@@ -992,7 +992,7 @@ static int ssl_hook_Access_classic(request_rec *r, SSLSrvConfigRec *sc, SSLDirCo
|
|||||||
|
|
||||||
/* Toggle the renegotiation state to allow the new
|
/* Toggle the renegotiation state to allow the new
|
||||||
* handshake to proceed. */
|
* handshake to proceed. */
|
||||||
sslconn->reneg_state = RENEG_ALLOW;
|
modssl_set_reneg_state(sslconn, RENEG_ALLOW);
|
||||||
|
|
||||||
SSL_renegotiate(ssl);
|
SSL_renegotiate(ssl);
|
||||||
SSL_do_handshake(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);
|
SSL_peek(ssl, peekbuf, 0);
|
||||||
|
|
||||||
sslconn->reneg_state = RENEG_REJECT;
|
modssl_set_reneg_state(sslconn, RENEG_REJECT);
|
||||||
|
|
||||||
if (!SSL_is_init_finished(ssl)) {
|
if (!SSL_is_init_finished(ssl)) {
|
||||||
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02261)
|
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)) {
|
(sc->server->auth.verify_mode != SSL_CVERIFY_UNSET)) {
|
||||||
int vmode_inplace, vmode_needed;
|
int vmode_inplace, vmode_needed;
|
||||||
int change_vmode = FALSE;
|
int change_vmode = FALSE;
|
||||||
int old_state, n, rc;
|
int n, rc;
|
||||||
|
|
||||||
vmode_inplace = SSL_get_verify_mode(ssl);
|
vmode_inplace = SSL_get_verify_mode(ssl);
|
||||||
vmode_needed = SSL_VERIFY_NONE;
|
vmode_needed = SSL_VERIFY_NONE;
|
||||||
@@ -1182,8 +1182,6 @@ static int ssl_hook_Access_modern(request_rec *r, SSLSrvConfigRec *sc, SSLDirCon
|
|||||||
return HTTP_FORBIDDEN;
|
return HTTP_FORBIDDEN;
|
||||||
}
|
}
|
||||||
|
|
||||||
old_state = sslconn->reneg_state;
|
|
||||||
sslconn->reneg_state = RENEG_ALLOW;
|
|
||||||
modssl_set_app_data2(ssl, r);
|
modssl_set_app_data2(ssl, r);
|
||||||
|
|
||||||
SSL_do_handshake(ssl);
|
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);
|
SSL_peek(ssl, peekbuf, 0);
|
||||||
|
|
||||||
sslconn->reneg_state = old_state;
|
|
||||||
modssl_set_app_data2(ssl, NULL);
|
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
|
* This callback function is executed while OpenSSL processes the SSL
|
||||||
* handshake and does SSL record layer stuff. It's used to trap
|
* handshake and does SSL record layer stuff. It's used to trap
|
||||||
* client-initiated renegotiations, and for dumping everything to the
|
* client-initiated renegotiations (where SSL_OP_NO_RENEGOTATION is
|
||||||
* log.
|
* not available), and for dumping everything to the log.
|
||||||
*/
|
*/
|
||||||
void ssl_callback_Info(const SSL *ssl, int where, int rc)
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* With TLS 1.3 this callback may be called multiple times on the first
|
#ifndef SSL_OP_NO_RENEGOTATION
|
||||||
* negotiation, so the below logic to detect renegotiations can't work.
|
/* With OpenSSL < 1.1.1 (implying TLS v1.2 or earlier), this
|
||||||
* Fortunately renegotiations are forbidden starting with TLS 1.3, and
|
* callback is used to block client-initiated renegotiation. With
|
||||||
* this is enforced by OpenSSL so there's nothing to be done here.
|
* TLSv1.3 it is unnecessary since renegotiation is forbidden at
|
||||||
*/
|
* protocol level. Otherwise (TLSv1.2 with OpenSSL >=1.1.1),
|
||||||
#if SSL_HAVE_PROTOCOL_TLSV1_3
|
* SSL_OP_NO_RENEGOTATION is used to block renegotiation. */
|
||||||
if (SSL_version(ssl) < TLS1_3_VERSION)
|
|
||||||
#endif
|
|
||||||
{
|
{
|
||||||
SSLConnRec *sslconn;
|
SSLConnRec *sslconn;
|
||||||
|
|
||||||
@@ -2312,6 +2307,7 @@ void ssl_callback_Info(const SSL *ssl, int where, int rc)
|
|||||||
sslconn->reneg_state = RENEG_REJECT;
|
sslconn->reneg_state = RENEG_REJECT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
s = mySrvFromConn(c);
|
s = mySrvFromConn(c);
|
||||||
if (s && APLOGdebug(s)) {
|
if (s && APLOGdebug(s)) {
|
||||||
|
@@ -500,6 +500,16 @@ typedef struct {
|
|||||||
apr_time_t source_mtime;
|
apr_time_t source_mtime;
|
||||||
} ssl_asn1_t;
|
} 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
|
* Define the mod_ssl per-module configuration structure
|
||||||
* (i.e. the global configuration for each httpd process)
|
* (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_SET_ERROR_MSG /* Need to set the error message */
|
||||||
} non_ssl_request;
|
} non_ssl_request;
|
||||||
|
|
||||||
/* Track the handshake/renegotiation state for the connection so
|
#ifndef SSL_OP_NO_RENEGOTATION
|
||||||
* that all client-initiated renegotiations can be rejected, as a
|
/* For OpenSSL < 1.1.1, track the handshake/renegotiation state
|
||||||
* partial fix for CVE-2009-3555. */
|
* for the connection to block client-initiated renegotiations.
|
||||||
enum {
|
* For OpenSSL >=1.1.1, the SSL_OP_NO_RENEGOTATION flag is used in
|
||||||
RENEG_INIT = 0, /* Before initial handshake */
|
* the SSL * options state with equivalent effect. */
|
||||||
RENEG_REJECT, /* After initial handshake; any client-initiated
|
modssl_reneg_state reneg_state;
|
||||||
* renegotiation should be rejected */
|
#endif
|
||||||
RENEG_ALLOW, /* A server-initiated renegotiation is taking
|
|
||||||
* place (as dictated by configuration) */
|
|
||||||
RENEG_ABORT /* Renegotiation initiated by client, abort the
|
|
||||||
* connection */
|
|
||||||
} reneg_state;
|
|
||||||
|
|
||||||
server_rec *server;
|
server_rec *server;
|
||||||
SSLDirConfigRec *dc;
|
SSLDirConfigRec *dc;
|
||||||
@@ -1146,6 +1151,9 @@ extern int ssl_running_on_valgrind;
|
|||||||
int ssl_is_challenge(conn_rec *c, const char *servername,
|
int ssl_is_challenge(conn_rec *c, const char *servername,
|
||||||
X509 **pcert, EVP_PKEY **pkey);
|
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 */
|
#endif /* SSL_PRIVATE_H */
|
||||||
/** @} */
|
/** @} */
|
||||||
|
|
||||||
|
@@ -511,3 +511,19 @@ char *modssl_SSL_SESSION_id2sz(IDCONST unsigned char *id, int idlen,
|
|||||||
|
|
||||||
return str;
|
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
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user