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.
This commit is contained in:
Olivier Houchard 2017-10-02 11:51:03 +02:00 committed by Willy Tarreau
parent eff9a9ef95
commit ccaa7de72e
3 changed files with 67 additions and 7 deletions

View 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 pass the first router, though it's still delivered to local networks. Do
not use it unless you fully understand how it works. 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. 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 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 from the cache or the ticket. So prefer "ssl_c_used" if you want to check if
current SSL session uses a client certificate. 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 ssl_fc_has_sni : boolean
This checks for the presence of a Server Name Indication TLS extension (SNI) 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 in an incoming connection was made over an SSL/TLS transport layer. Returns

View File

@ -95,6 +95,7 @@
#include <proto/openssl-compat.h> #include <proto/openssl-compat.h>
#include <proto/pattern.h> #include <proto/pattern.h>
#include <proto/proto_tcp.h> #include <proto/proto_tcp.h>
#include <proto/proto_http.h>
#include <proto/server.h> #include <proto/server.h>
#include <proto/stream_interface.h> #include <proto/stream_interface.h>
#include <proto/log.h> #include <proto/log.h>
@ -4892,13 +4893,6 @@ int ssl_sock_handshake(struct connection *conn, unsigned int flag)
*/ */
if (global_ssl.async) if (global_ssl.async)
SSL_clear_mode(conn->xprt_ctx, SSL_MODE_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 #endif
/* Handshake succeeded */ /* Handshake succeeded */
if (!SSL_session_reused(conn->xprt_ctx)) { 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 *****/ /***** 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 */ /* boolean, returns true if client cert was present */
static int static int
smp_fetch_ssl_fc_has_crt(const struct arg *args, struct sample *smp, const char *kw, void *private) 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_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_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_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_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 }, { "ssl_fc_is_resumed", smp_fetch_ssl_fc_is_resumed, 0, NULL, SMP_T_BOOL, SMP_USE_L5CLI },
#ifdef OPENSSL_NPN_NEGOTIATED #ifdef OPENSSL_NPN_NEGOTIATED
@ -8317,6 +8328,34 @@ static struct xprt_ops ssl_sock = {
.name = "SSL", .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) #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) 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 #endif
/* Load SSL string for the verbose & debug mode. */ /* Load SSL string for the verbose & debug mode. */
ERR_load_SSL_strings(); ERR_load_SSL_strings();
http_req_keywords_register(&http_req_actions);
} }
#ifndef OPENSSL_NO_ENGINE #ifndef OPENSSL_NO_ENGINE

View File

@ -572,6 +572,16 @@ static int si_conn_wake_cb(struct connection *conn)
if (conn->flags & CO_FL_ERROR) if (conn->flags & CO_FL_ERROR)
si->flags |= SI_FL_ERR; 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) && if ((si->state < SI_ST_EST) &&
(conn->flags & (CO_FL_CONNECTED | CO_FL_HANDSHAKE)) == CO_FL_CONNECTED) { (conn->flags & (CO_FL_CONNECTED | CO_FL_HANDSHAKE)) == CO_FL_CONNECTED) {
si->exp = TICK_ETERNITY; si->exp = TICK_ETERNITY;