MEDIUM: ssl: implement a workaround for the OpenSSL heartbleed attack

Using the previous callback, it's trivial to block the heartbeat attack,
first we control the message length, then we emit an SSL error if it is
out of bounds. A special log is emitted, indicating that a heartbleed
attack was stopped so that they are not confused with other failures.

That way, haproxy can protect itself even when running on an unpatched
SSL stack. Tests performed with openssl-1.0.1c indicate a total success.
This commit is contained in:
Willy Tarreau 2014-04-25 20:02:39 +02:00
parent 29f037d872
commit f51c6989b0
3 changed files with 33 additions and 4 deletions

View File

@ -582,6 +582,7 @@ static inline const char *conn_err_code_str(struct connection *c)
case CO_ER_SSL_CRT_FAIL: return "SSL client certificate not trusted";
case CO_ER_SSL_HANDSHAKE: return "SSL handshake failure";
case CO_ER_SSL_HANDSHAKE_HB: return "SSL handshake failure after heartbeat";
case CO_ER_SSL_KILLED_HB: return "Stopped a TLSv1 heartbeat attack (CVE-2014-0160)";
case CO_ER_SSL_NO_TARGET: return "Attempt to use SSL on an unknown target (internal error)";
}
return NULL;

View File

@ -163,7 +163,8 @@ enum {
CO_ER_SSL_CRT_FAIL, /* client cert verification failed on the certificate */
CO_ER_SSL_HANDSHAKE, /* SSL error during handshake */
CO_ER_SSL_HANDSHAKE_HB, /* SSL error during handshake with heartbeat present */
CO_ER_SSL_NO_TARGET, /* unkonwn target (not client nor server) */
CO_ER_SSL_KILLED_HB, /* Stopped a TLSv1 heartbeat attack (CVE-2014-0160) */
CO_ER_SSL_NO_TARGET, /* unknown target (not client nor server) */
};
/* source address settings for outgoing connections */

View File

@ -190,8 +190,33 @@ void ssl_sock_msgcbk(int write_p, int version, int content_type, const void *buf
#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))
if ((content_type == TLS1_RT_HEARTBEAT) && (write_p == 0)) {
const unsigned char *p = buf;
unsigned int payload;
conn->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)
goto kill_it;
payload = (p[1] * 256) + p[2];
if (1 + 2 + payload + 16 <= len)
return; /* OK no problem */
kill_it:
/* we have a clear heartbleed attack (CVE-2014-0160),
* we can't know if the SSL stack is patched, so better
* kill the connection before OpenSSL tries to send the
* bytes back to the attacker. 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.
*/
SSLerr(SSL_F_TLS1_HEARTBEAT, SSL_R_SSL_HANDSHAKE_FAILURE);
return;
}
#endif
}
@ -1443,7 +1468,8 @@ int ssl_sock_handshake(struct connection *conn, unsigned int flag)
*/
conn_drain(conn);
if (!conn->err_code)
conn->err_code = CO_ER_SSL_HANDSHAKE;
conn->err_code = (conn->xprt_st & SSL_SOCK_RECV_HEARTBEAT) ?
CO_ER_SSL_KILLED_HB : CO_ER_SSL_HANDSHAKE;
goto out_error;
}
}
@ -1508,7 +1534,8 @@ int ssl_sock_handshake(struct connection *conn, unsigned int flag)
*/
conn_drain(conn);
if (!conn->err_code)
conn->err_code = CO_ER_SSL_HANDSHAKE;
conn->err_code = (conn->xprt_st & SSL_SOCK_RECV_HEARTBEAT) ?
CO_ER_SSL_KILLED_HB : CO_ER_SSL_HANDSHAKE;
goto out_error;
}
}