diff options
Diffstat (limited to 'src/quic_ssl.c')
-rw-r--r-- | src/quic_ssl.c | 144 |
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); |