From: Olivier Houchard Date: Mon, 2 Oct 2017 09:51:03 +0000 (+0200) Subject: MINOR: ssl/proto_http: Add keywords to take care of early data. X-Git-Tag: v1.8-rc1~211 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ccaa7de72e847a920d9cbe29e6959af46450e4d9;p=thirdparty%2Fhaproxy.git MINOR: ssl/proto_http: Add keywords to take care of early data. 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. --- diff --git a/doc/configuration.txt b/doc/configuration.txt index 7ab0f3c595..cf0d696064 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -4163,6 +4163,10 @@ http-request { allow | auth [realm ] | redirect | 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 diff --git a/src/ssl_sock.c b/src/ssl_sock.c index 579a25b3bf..844be0a0e8 100644 --- a/src/ssl_sock.c +++ b/src/ssl_sock.c @@ -95,6 +95,7 @@ #include #include #include +#include #include #include #include @@ -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 diff --git a/src/stream_interface.c b/src/stream_interface.c index 53b201c085..a5463b7516 100644 --- a/src/stream_interface.c +++ b/src/stream_interface.c @@ -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;