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 7ab0f3c..cf0d696 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -4163,6 +4163,10 @@
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 @@
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 579a25b..844be0a 100644
--- a/src/ssl_sock.c
+++ b/src/ssl_sock.c
@@ -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>
@@ -4893,13 +4894,6 @@
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)) {
if (objt_server(conn->target)) {
@@ -5648,6 +5642,22 @@
/***** 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 @@
{ "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 @@
.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 @@
#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 53b201c..a5463b7 100644
--- a/src/stream_interface.c
+++ b/src/stream_interface.c
@@ -572,6 +572,16 @@
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;