]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: ssl: implement a workaround for the OpenSSL heartbleed attack
authorWilly Tarreau <w@1wt.eu>
Fri, 25 Apr 2014 18:02:39 +0000 (20:02 +0200)
committerWilly Tarreau <w@1wt.eu>
Fri, 25 Apr 2014 18:06:33 +0000 (20:06 +0200)
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.

include/proto/connection.h
include/types/connection.h
src/ssl_sock.c

index dde9b8c7e53698a44bb518e00d6fa815fb1a8387..10bc4d8ac32ede59cf67913e9b319c238027d68d 100644 (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;
index 84248c98ec214ae9ea06608378e57e17478a2d76..83ac432b340a5948b8c2cbc88d8575b2c47578e7 100644 (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 */
index 83c75f1d25ca960ff26d4df0809cfa7a4af6cf5c..b202ea57d799939a76c862f8d541e98571d2b977 100644 (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;
                }
        }