]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: ssl: split ssl_sock_msgcbk() and use a new callback mechanism
authorDragan Dosen <ddosen@haproxy.com>
Mon, 11 May 2020 13:51:45 +0000 (15:51 +0200)
committerWilliam Lallemand <wlallemand@haproxy.org>
Thu, 14 May 2020 11:13:14 +0000 (13:13 +0200)
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.

src/ssl_sock.c

index 4a4ca9b3a54c8d744aaf060ae2f300e9cb93d336..232b10b556dfcf5ee8720e4e1bfbf4800c6a8f3a 100644 (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().
         */