MEDIUM: ssl: split ssl_sock_msgcbk() and use a new callback mechanism

Make use of ssl_sock_register_msg_callback(). Function ssl_sock_msgcbk()
is now split into two dedicated functions for heartbeat and clienthello.
They are both registered by using a new callback mechanism for SSL/TLS
protocol messages.
This commit is contained in:
Dragan Dosen 2020-05-11 15:51:45 +02:00 committed by William Lallemand
parent 1e7ed04665
commit 9ac9809cb9

View File

@ -629,6 +629,17 @@ static struct eb_root *sh_ssl_sess_tree; /* ssl shared session tree */
#define sh_ssl_sess_tree_lookup(k) (struct sh_ssl_sess_hdr *)ebmb_lookup(sh_ssl_sess_tree, \
(k), SSL_MAX_SSL_SESSION_ID_LENGTH);
/* Dedicated callback functions for heartbeat and clienthello.
*/
#ifdef TLS1_RT_HEARTBEAT
static void ssl_sock_parse_heartbeat(struct connection *conn, int write_p, int version,
int content_type, const void *buf, size_t len,
SSL *ssl);
#endif
static void ssl_sock_parse_clienthello(struct connection *conn, int write_p, int version,
int content_type, const void *buf, size_t len,
SSL *ssl);
/* List head of all registered SSL/TLS protocol message callbacks. */
struct list ssl_sock_msg_callbacks = LIST_HEAD_INIT(ssl_sock_msg_callbacks);
@ -656,6 +667,21 @@ int ssl_sock_register_msg_callback(ssl_sock_msg_callback_func func)
return 1;
}
/* Used to register dedicated SSL/TLS protocol message callbacks.
*/
static int ssl_sock_register_msg_callbacks(void)
{
#ifdef TLS1_RT_HEARTBEAT
if (!ssl_sock_register_msg_callback(ssl_sock_parse_heartbeat))
return ERR_ABORT;
#endif
if (global_ssl.capture_cipherlist > 0) {
if (!ssl_sock_register_msg_callback(ssl_sock_parse_clienthello))
return ERR_ABORT;
}
return 0;
}
/* Used to free all SSL/TLS protocol message callbacks that were
* registered by using ssl_sock_register_msg_callback().
*/
@ -1815,9 +1841,52 @@ int ssl_sock_bind_verifycbk(int ok, X509_STORE_CTX *x_store)
return 0;
}
static inline
void ssl_sock_parse_clienthello(int write_p, int version, int content_type,
const void *buf, size_t len, SSL *ssl)
#ifdef TLS1_RT_HEARTBEAT
static void ssl_sock_parse_heartbeat(struct connection *conn, int write_p, int version,
int content_type, const void *buf, size_t len,
SSL *ssl)
{
/* test heartbeat received (write_p is set to 0
for a received record) */
if ((content_type == TLS1_RT_HEARTBEAT) && (write_p == 0)) {
struct ssl_sock_ctx *ctx = conn->xprt_ctx;
const unsigned char *p = buf;
unsigned int payload;
ctx->xprt_st |= SSL_SOCK_RECV_HEARTBEAT;
/* Check if this is a CVE-2014-0160 exploitation attempt. */
if (*p != TLS1_HB_REQUEST)
return;
if (len < 1 + 2 + 16) /* 1 type + 2 size + 0 payload + 16 padding */
goto kill_it;
payload = (p[1] * 256) + p[2];
if (3 + payload + 16 <= len)
return; /* OK no problem */
kill_it:
/* We have a clear heartbleed attack (CVE-2014-0160), the
* advertised payload is larger than the advertised packet
* length, so we have garbage in the buffer between the
* payload and the end of the buffer (p+len). We can't know
* if the SSL stack is patched, and we don't know if we can
* safely wipe out the area between p+3+len and payload.
* So instead, we prevent the response from being sent by
* setting the max_send_fragment to 0 and we report an SSL
* error, which will kill this connection. It will be reported
* above as SSL_ERROR_SSL while an other handshake failure with
* a heartbeat message will be reported as SSL_ERROR_SYSCALL.
*/
ssl->max_send_fragment = 0;
SSLerr(SSL_F_TLS1_HEARTBEAT, SSL_R_SSL_HANDSHAKE_FAILURE);
}
}
#endif
static void ssl_sock_parse_clienthello(struct connection *conn, int write_p, int version,
int content_type, const void *buf, size_t len,
SSL *ssl)
{
struct ssl_capture *capture;
unsigned char *msg;
@ -1930,47 +1999,6 @@ void ssl_sock_msgcbk(int write_p, int version, int content_type, const void *buf
struct connection *conn = SSL_get_ex_data(ssl, ssl_app_data_index);
struct ssl_sock_msg_callback *cbk;
#ifdef TLS1_RT_HEARTBEAT
/* test heartbeat received (write_p is set to 0
for a received record) */
if ((content_type == TLS1_RT_HEARTBEAT) && (write_p == 0)) {
struct ssl_sock_ctx *ctx = conn->xprt_ctx;
const unsigned char *p = buf;
unsigned int payload;
ctx->xprt_st |= SSL_SOCK_RECV_HEARTBEAT;
/* Check if this is a CVE-2014-0160 exploitation attempt. */
if (*p != TLS1_HB_REQUEST)
return;
if (len < 1 + 2 + 16) /* 1 type + 2 size + 0 payload + 16 padding */
goto kill_it;
payload = (p[1] * 256) + p[2];
if (3 + payload + 16 <= len)
return; /* OK no problem */
kill_it:
/* We have a clear heartbleed attack (CVE-2014-0160), the
* advertised payload is larger than the advertised packet
* length, so we have garbage in the buffer between the
* payload and the end of the buffer (p+len). We can't know
* if the SSL stack is patched, and we don't know if we can
* safely wipe out the area between p+3+len and payload.
* So instead, we prevent the response from being sent by
* setting the max_send_fragment to 0 and we report an SSL
* error, which will kill this connection. It will be reported
* above as SSL_ERROR_SSL while an other handshake failure with
* a heartbeat message will be reported as SSL_ERROR_SYSCALL.
*/
ssl->max_send_fragment = 0;
SSLerr(SSL_F_TLS1_HEARTBEAT, SSL_R_SSL_HANDSHAKE_FAILURE);
return;
}
#endif
if (global_ssl.capture_cipherlist > 0)
ssl_sock_parse_clienthello(write_p, version, content_type, buf, len, ssl);
/* Try to call all callback functions that were registered by using
* ssl_sock_register_msg_callback().
*/
@ -13150,6 +13178,11 @@ static void __ssl_sock_init(void)
HA_SPIN_INIT(&ckch_lock);
/* Try to register dedicated SSL/TLS protocol message callbacks for
* heartbleed attack (CVE-2014-0160) and clienthello.
*/
hap_register_post_check(ssl_sock_register_msg_callbacks);
/* Try to free all callbacks that were registered by using
* ssl_sock_register_msg_callback().
*/