]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: ssl/proto_http: Add keywords to take care of early data.
authorOlivier Houchard <ohouchard@haproxy.com>
Mon, 2 Oct 2017 09:51:03 +0000 (11:51 +0200)
committerWilly Tarreau <w@1wt.eu>
Fri, 27 Oct 2017 11:32:22 +0000 (13:32 +0200)
Add a new sample fetch, "ssl_fc_has_early", a boolean that will be true
if early data were sent, and a new action, "wait-for-handshake", if used,
the request won't be forwarded until the SSL handshake is done.

doc/configuration.txt
src/ssl_sock.c
src/stream_interface.c

index 7ab0f3c59512c454f2cb31f20dca6f6c11038a17..cf0d69606418bee216e9f08034d608a1c65f2ea6 100644 (file)
@@ -4163,6 +4163,10 @@ http-request { allow | auth [realm <realm>] | redirect <rule> |
       pass the first router, though it's still delivered to local networks. Do
       not use it unless you fully understand how it works.
 
+    - "wait-for-handshake" : this will delay the processing of the request
+      until the SSL handshake happened. This is mostly useful to delay
+      processing early data until we're sure they are valid.
+
   There is no limit to the number of http-request statements per instance.
 
   It is important to know that http-request rules are processed very early in
@@ -14252,6 +14256,11 @@ ssl_fc_has_crt : boolean
   from the cache or the ticket. So prefer "ssl_c_used" if you want to check if
   current SSL session uses a client certificate.
 
+ssl_fc_has_early : boolean
+  Returns true if early data were sent, and the handshake didn't happen yet. As
+  it has security implications, it is useful to be able to refuse those, or
+  wait until the handshake happened.
+
 ssl_fc_has_sni : boolean
   This checks for the presence of a Server Name Indication TLS extension (SNI)
   in an incoming connection was made over an SSL/TLS transport layer. Returns
index 579a25b3bf782b759d999b945a205407c284dba3..844be0a0e8dea42c28c8f7c55aedd58c6724dfaf 100644 (file)
@@ -95,6 +95,7 @@
 #include <proto/openssl-compat.h>
 #include <proto/pattern.h>
 #include <proto/proto_tcp.h>
+#include <proto/proto_http.h>
 #include <proto/server.h>
 #include <proto/stream_interface.h>
 #include <proto/log.h>
@@ -4892,13 +4893,6 @@ reneg_ok:
         */
        if (global_ssl.async)
                SSL_clear_mode(conn->xprt_ctx, SSL_MODE_ASYNC);
-#endif
-#if OPENSSL_VERSION_NUMBER >= 0x10101000L
-       /* Once the handshake succeeded, we can consider the early data
-        * as valid.
-        */
-       if (conn->flags & CO_FL_EARLY_DATA)
-               conn->flags &= ~CO_FL_EARLY_DATA;
 #endif
        /* Handshake succeeded */
        if (!SSL_session_reused(conn->xprt_ctx)) {
@@ -5648,6 +5642,22 @@ static int ssl_sock_get_alpn(const struct connection *conn, const char **str, in
 
 /***** Below are some sample fetching functions for ACL/patterns *****/
 
+static int
+smp_fetch_ssl_fc_has_early(const struct arg *args, struct sample *smp, const char *kw, void *private)
+{
+       struct connection *conn;
+
+       conn = objt_conn(smp->sess->origin);
+       if (!conn || conn->xprt != &ssl_sock)
+               return 0;
+
+       smp->flags = 0;
+       smp->data.type = SMP_T_BOOL;
+       smp->data.u.sint = (conn->flags & CO_FL_EARLY_DATA) ? 1 : 0;
+
+       return 1;
+}
+
 /* boolean, returns true if client cert was present */
 static int
 smp_fetch_ssl_fc_has_crt(const struct arg *args, struct sample *smp, const char *kw, void *private)
@@ -8139,6 +8149,7 @@ static struct sample_fetch_kw_list sample_fetch_keywords = {ILH, {
        { "ssl_fc_alg_keysize",     smp_fetch_ssl_fc_alg_keysize, 0,                   NULL,    SMP_T_SINT, SMP_USE_L5CLI },
        { "ssl_fc_cipher",          smp_fetch_ssl_fc_cipher,      0,                   NULL,    SMP_T_STR,  SMP_USE_L5CLI },
        { "ssl_fc_has_crt",         smp_fetch_ssl_fc_has_crt,     0,                   NULL,    SMP_T_BOOL, SMP_USE_L5CLI },
+       { "ssl_fc_has_early",       smp_fetch_ssl_fc_has_early,   0,                   NULL,    SMP_T_BOOL, SMP_USE_L5CLI },
        { "ssl_fc_has_sni",         smp_fetch_ssl_fc_has_sni,     0,                   NULL,    SMP_T_BOOL, SMP_USE_L5CLI },
        { "ssl_fc_is_resumed",      smp_fetch_ssl_fc_is_resumed,  0,                   NULL,    SMP_T_BOOL, SMP_USE_L5CLI },
 #ifdef OPENSSL_NPN_NEGOTIATED
@@ -8317,6 +8328,34 @@ static struct xprt_ops ssl_sock = {
        .name     = "SSL",
 };
 
+enum act_return ssl_action_wait_for_hs(struct act_rule *rule, struct proxy *px,
+                                       struct session *sess, struct stream *s, int flags)
+{
+       struct connection *conn;
+
+       conn = objt_conn(sess->origin);
+
+       if (conn) {
+               if (conn->flags & (CO_FL_EARLY_SSL_HS | CO_FL_SSL_WAIT_HS)) {
+                       s->req.flags |= CF_READ_NULL;
+                       return ACT_RET_YIELD;
+               }
+       }
+       return (ACT_RET_CONT);
+}
+
+static enum act_parse_ret ssl_parse_wait_for_hs(const char **args, int *orig_arg, struct proxy *px, struct act_rule *rule, char **err)
+{
+       rule->action_ptr = ssl_action_wait_for_hs;
+
+       return ACT_RET_PRS_OK;
+}
+
+static struct action_kw_list http_req_actions = {ILH, {
+       { "wait-for-handshake", ssl_parse_wait_for_hs },
+       { /* END */ }
+}};
+
 #if (OPENSSL_VERSION_NUMBER >= 0x1000200fL && !defined OPENSSL_NO_TLSEXT && !defined OPENSSL_IS_BORINGSSL && !defined LIBRESSL_VERSION_NUMBER)
 
 static void ssl_sock_sctl_free_func(void *parent, void *ptr, CRYPTO_EX_DATA *ad, int idx, long argl, void *argp)
@@ -8419,6 +8458,8 @@ static void __ssl_sock_init(void)
 #endif
        /* Load SSL string for the verbose & debug mode. */
        ERR_load_SSL_strings();
+
+       http_req_keywords_register(&http_req_actions);
 }
 
 #ifndef OPENSSL_NO_ENGINE
index 53b201c085f45501b4a9ac173b4c9f30e3ca4edc..a5463b7516e252365ae36dacc28c581b6cca41c8 100644 (file)
@@ -572,6 +572,16 @@ static int si_conn_wake_cb(struct connection *conn)
        if (conn->flags & CO_FL_ERROR)
                si->flags |= SI_FL_ERR;
 
+       /* If we had early data, and the handshake ended, then
+        * we can remove the flag, and attempt to wake the task up,
+        * in the event there's an analyser waiting for the end of
+        * the handshake.
+        */
+       if ((conn->flags & (CO_FL_EARLY_DATA | CO_FL_EARLY_SSL_HS)) == CO_FL_EARLY_DATA) {
+               conn->flags &= ~CO_FL_EARLY_DATA;
+               task_wakeup(si_task(si), TASK_WOKEN_MSG);
+       }
+
        if ((si->state < SI_ST_EST) &&
            (conn->flags & (CO_FL_CONNECTED | CO_FL_HANDSHAKE)) == CO_FL_CONNECTED) {
                si->exp = TICK_ETERNITY;