summaryrefslogtreecommitdiffstats
path: root/src/quic_ssl.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/quic_ssl.c')
-rw-r--r--src/quic_ssl.c144
1 files changed, 121 insertions, 23 deletions
diff --git a/src/quic_ssl.c b/src/quic_ssl.c
index 85b6717..66eb68e 100644
--- a/src/quic_ssl.c
+++ b/src/quic_ssl.c
@@ -2,7 +2,6 @@
#include <haproxy/ncbuf.h>
#include <haproxy/proxy.h>
#include <haproxy/quic_conn.h>
-#include <haproxy/quic_rx.h>
#include <haproxy/quic_sock.h>
#include <haproxy/quic_ssl.h>
#include <haproxy/quic_tls.h>
@@ -442,24 +441,25 @@ int ssl_quic_initial_ctx(struct bind_conf *bind_conf)
ctx = SSL_CTX_new(TLS_server_method());
bind_conf->initial_ctx = ctx;
+ if (global_ssl.security_level > -1)
+ SSL_CTX_set_security_level(ctx, global_ssl.security_level);
SSL_CTX_set_options(ctx, options);
SSL_CTX_set_mode(ctx, SSL_MODE_RELEASE_BUFFERS);
SSL_CTX_set_min_proto_version(ctx, TLS1_3_VERSION);
SSL_CTX_set_max_proto_version(ctx, TLS1_3_VERSION);
-#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
-# if defined(HAVE_SSL_CLIENT_HELLO_CB)
-# if defined(SSL_OP_NO_ANTI_REPLAY)
if (bind_conf->ssl_conf.early_data) {
- SSL_CTX_set_options(ctx, SSL_OP_NO_ANTI_REPLAY);
-# if defined(USE_QUIC_OPENSSL_COMPAT) || defined(OPENSSL_IS_AWSLC)
- ha_warning("Binding [%s:%d] for %s %s: 0-RTT is not supported in limited QUIC compatibility mode, ignored.\n",
+#if !defined(HAVE_SSL_0RTT_QUIC)
+ ha_warning("Binding [%s:%d] for %s %s: 0-RTT with QUIC is not supported by this SSL library, ignored.\n",
bind_conf->file, bind_conf->line, proxy_type_str(bind_conf->frontend), bind_conf->frontend->id);
-# else
+#else
+ SSL_CTX_set_options(ctx, SSL_OP_NO_ANTI_REPLAY);
SSL_CTX_set_max_early_data(ctx, 0xffffffff);
-# endif /* ! USE_QUIC_OPENSSL_COMPAT */
+#endif /* ! HAVE_SSL_0RTT_QUIC */
}
-# endif /* !SSL_OP_NO_ANTI_REPLAY */
+
+#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
+# if defined(HAVE_SSL_CLIENT_HELLO_CB)
SSL_CTX_set_client_hello_cb(ctx, ssl_sock_switchctx_cbk, NULL);
SSL_CTX_set_tlsext_servername_callback(ctx, ssl_sock_switchctx_err_cbk);
# else /* ! HAVE_SSL_CLIENT_HELLO_CB */
@@ -502,10 +502,10 @@ static forceinline void qc_ssl_dump_errors(struct connection *conn)
* Remaining parameter are there for debugging purposes.
* Return 1 if succeeded, 0 if not.
*/
-int qc_ssl_provide_quic_data(struct ncbuf *ncbuf,
- enum ssl_encryption_level_t level,
- struct ssl_sock_ctx *ctx,
- const unsigned char *data, size_t len)
+static int qc_ssl_provide_quic_data(struct ncbuf *ncbuf,
+ enum ssl_encryption_level_t level,
+ struct ssl_sock_ctx *ctx,
+ const unsigned char *data, size_t len)
{
#ifdef DEBUG_STRICT
enum ncb_ret ncb_ret;
@@ -557,6 +557,39 @@ int qc_ssl_provide_quic_data(struct ncbuf *ncbuf,
ERR_clear_error();
goto leave;
}
+#if defined(LIBRESSL_VERSION_NUMBER)
+ else if (qc->flags & QUIC_FL_CONN_IMMEDIATE_CLOSE) {
+ /* Some libressl versions emit TLS alerts without making the handshake
+ * (SSL_do_handshake()) fail. This is at least the case for
+ * libressl-3.9.0 when forcing the TLS cipher to TLS_AES_128_CCM_SHA256.
+ */
+ TRACE_ERROR("SSL handshake error", QUIC_EV_CONN_IO_CB, qc, &state, &ssl_err);
+ HA_ATOMIC_INC(&qc->prx_counters->hdshk_fail);
+ goto leave;
+ }
+#endif
+
+#if defined(OPENSSL_IS_AWSLC)
+ /* As a server, if early data is accepted, SSL_do_handshake will
+ * complete as soon as the ClientHello is processed and server flight sent.
+ * SSL_write may be used to send half-RTT data. SSL_read will consume early
+ * data and transition to 1-RTT data as appropriate. Prior to the
+ * transition, SSL_in_init will report the handshake is still in progress.
+ * Callers may use it or SSL_in_early_data to defer or reject requests
+ * as needed.
+ * (see https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#Early-data)
+ */
+
+ /* If we do not returned here, the handshake is considered as completed/confirmed.
+ * This has as bad side effect to discard the Handshake packet number space,
+ * so without sending the Handshake level CRYPTO data.
+ */
+ if (SSL_in_early_data(ctx->ssl)) {
+ TRACE_PROTO("SSL handshake in progrees with early data",
+ QUIC_EV_CONN_IO_CB, qc, &state, &ssl_err);
+ goto out;
+ }
+#endif
TRACE_PROTO("SSL handshake OK", QUIC_EV_CONN_IO_CB, qc, &state);
@@ -572,8 +605,17 @@ int qc_ssl_provide_quic_data(struct ncbuf *ncbuf,
if (qc_is_listener(ctx->qc)) {
qc->flags |= QUIC_FL_CONN_NEED_POST_HANDSHAKE_FRMS;
qc->state = QUIC_HS_ST_CONFIRMED;
- /* The connection is ready to be accepted. */
- quic_accept_push_qc(qc);
+
+ if (!(qc->flags & QUIC_FL_CONN_ACCEPT_REGISTERED)) {
+ quic_accept_push_qc(qc);
+ }
+ else {
+ /* Connection already accepted if 0-RTT used.
+ * In this case, schedule quic-conn to ensure
+ * post-handshake frames are emitted.
+ */
+ tasklet_wakeup(qc->wait_event.tasklet);
+ }
BUG_ON(qc->li->rx.quic_curr_handshake == 0);
HA_ATOMIC_DEC(&qc->li->rx.quic_curr_handshake);
@@ -636,6 +678,8 @@ int qc_ssl_provide_all_quic_data(struct quic_conn *qc, struct ssl_sock_ctx *ctx)
{
int ret = 0;
struct quic_enc_level *qel;
+ struct ncbuf *ncbuf;
+ ncb_sz_t data;
TRACE_ENTER(QUIC_EV_CONN_PHPKTS, qc);
list_for_each_entry(qel, &qc->qel_list, list) {
@@ -644,8 +688,27 @@ int qc_ssl_provide_all_quic_data(struct quic_conn *qc, struct ssl_sock_ctx *ctx)
if (!cstream)
continue;
- if (!qc_treat_rx_crypto_frms(qc, qel, ctx))
- goto leave;
+ ncbuf = &cstream->rx.ncbuf;
+ if (ncb_is_null(ncbuf))
+ continue;
+
+ /* TODO not working if buffer is wrapping */
+ while ((data = ncb_data(ncbuf, 0))) {
+ const unsigned char *cdata = (const unsigned char *)ncb_head(ncbuf);
+
+ if (!qc_ssl_provide_quic_data(&qel->cstream->rx.ncbuf, qel->level,
+ ctx, cdata, data))
+ goto leave;
+
+ cstream->rx.offset += data;
+ TRACE_DEVEL("buffered crypto data were provided to TLS stack",
+ QUIC_EV_CONN_PHPKTS, qc, qel);
+ }
+
+ if (!ncb_is_null(ncbuf) && ncb_is_empty(ncbuf)) {
+ TRACE_DEVEL("freeing crypto buf", QUIC_EV_CONN_PHPKTS, qc, qel);
+ quic_free_ncbuf(ncbuf);
+ }
}
ret = 1;
@@ -696,6 +759,43 @@ static int qc_ssl_sess_init(struct quic_conn *qc, SSL_CTX *ssl_ctx, SSL **ssl)
return ret;
}
+#ifdef HAVE_SSL_0RTT_QUIC
+
+/* Enable early data for <ssl> QUIC TLS session.
+ * Return 1 if succeeded, 0 if not.
+ */
+static int qc_set_quic_early_data_enabled(struct quic_conn *qc, SSL *ssl)
+{
+#if defined(OPENSSL_IS_AWSLC)
+ struct quic_transport_params p = {0};
+ unsigned char buf[128];
+ size_t len;
+
+ /* Apply default values to <p> transport parameters. */
+ quic_transport_params_init(&p, 1);
+ /* The stateless_reset_token transport parameter is not needed. */
+ p.with_stateless_reset_token = 0;
+ len = quic_transport_params_encode(buf, buf + sizeof buf, &p, NULL, 1);
+ if (!len) {
+ TRACE_ERROR("quic_transport_params_encode() failed", QUIC_EV_CONN_RWSEC, qc);
+ return 0;
+ }
+
+ /* XXX TODO: Should also add the application settings. XXX */
+ if (!SSL_set_quic_early_data_context(ssl, buf, len)) {
+ TRACE_ERROR("SSL_set_quic_early_data_context() failed", QUIC_EV_CONN_RWSEC, qc);
+ return 0;
+ }
+
+ SSL_set_early_data_enabled(ssl, 1);
+#else
+ SSL_set_quic_early_data_enabled(ssl, 1);
+#endif
+
+ return 1;
+}
+#endif // HAVE_SSL_0RTT_QUIC
+
/* Allocate the ssl_sock_ctx from connection <qc>. This creates the tasklet
* used to process <qc> received packets. The allocated context is stored in
* <qc.xprt_ctx>.
@@ -731,12 +831,10 @@ int qc_alloc_ssl_sock_ctx(struct quic_conn *qc)
if (qc_is_listener(qc)) {
if (qc_ssl_sess_init(qc, bc->initial_ctx, &ctx->ssl) == -1)
goto err;
-#if (HA_OPENSSL_VERSION_NUMBER >= 0x10101000L) && !defined(OPENSSL_IS_AWSLC)
-#ifndef USE_QUIC_OPENSSL_COMPAT
+#if (HA_OPENSSL_VERSION_NUMBER >= 0x10101000L) && defined(HAVE_SSL_0RTT_QUIC)
/* Enabling 0-RTT */
- if (bc->ssl_conf.early_data)
- SSL_set_quic_early_data_enabled(ctx->ssl, 1);
-#endif
+ if (bc->ssl_conf.early_data && !qc_set_quic_early_data_enabled(qc, ctx->ssl))
+ goto err;
#endif
SSL_set_accept_state(ctx->ssl);