From: Olivier Houchard Date: Fri, 6 Mar 2026 12:34:37 +0000 (+0100) Subject: BUG/MEDIUM: ssl: Handle receiving early data with BoringSSL/AWS-LC X-Git-Tag: v3.4-dev7~79 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=76ba026548975a6d1bc23d1344807c64d994bf1e;p=thirdparty%2Fhaproxy.git BUG/MEDIUM: ssl: Handle receiving early data with BoringSSL/AWS-LC The API for early data is a bit different with BoringSSL and AWS-LC than it is for OpenSSL. As it was implemented, early data would be accepted, but would not be processed until the handshake is done. Change that by doing something similar to what OpenSSL does, and, if 0RTT has been enabled on the listener, use SSL_read() to try to get early data before starting the handshake, and if there's any, provide them to the mux the same way it is done for OpenSSL. That replaces a bunch of #ifdef SSL_READ_EARLY_DATA_SUCCESS by something specific to OpenSSL has to be done. This should be backported to 3.3. --- diff --git a/src/ssl_sock.c b/src/ssl_sock.c index 2e38dc064..839aed94c 100644 --- a/src/ssl_sock.c +++ b/src/ssl_sock.c @@ -5883,15 +5883,17 @@ static int ssl_sock_init(struct connection *conn, void **xprt_ctx) &ctx->ssl, &ctx->bio, ha_meth, ctx) == -1) goto err; -#ifdef SSL_READ_EARLY_DATA_SUCCESS +#ifdef HAVE_SSL_0RTT if (bc->ssl_conf.early_data) { b_alloc(&ctx->early_buf, DB_MUX_RX); +#if defined(SSL_READ_EARLY_DATA_SUCCESS) SSL_set_max_early_data(ctx->ssl, /* Only allow early data if we managed to allocate * a buffer. */ (!b_is_null(&ctx->early_buf)) ? ctx->early_buf.size - global.tune.maxrewrite : 0); +#endif } #endif @@ -5899,7 +5901,7 @@ static int ssl_sock_init(struct connection *conn, void **xprt_ctx) /* leave init state and start handshake */ conn->flags |= CO_FL_SSL_WAIT_HS | CO_FL_WAIT_L6_CONN; -#ifdef SSL_READ_EARLY_DATA_SUCCESS +#ifdef HAVE_SSL_0RTT if (bc->ssl_conf.early_data) conn->flags |= CO_FL_EARLY_SSL_HS; #endif @@ -6044,16 +6046,35 @@ static int ssl_sock_handshake(struct connection *conn, unsigned int flag) skerr != 0) goto out_error; -#ifdef SSL_READ_EARLY_DATA_SUCCESS +#ifdef HAVE_SSL_0RTT /* * Check if we have early data. If we do, we have to read them * before SSL_do_handshake() is called, And there's no way to * detect early data, except to try to read them */ if (conn->flags & CO_FL_EARLY_SSL_HS) { - size_t read_data = 0; while (1) { +#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC) + ret = SSL_read(ctx->ssl, b_tail(&ctx->early_buf), + b_room(&ctx->early_buf)); + if (ret > 0) { + conn->flags |= CO_FL_EARLY_DATA; + b_add(&ctx->early_buf, ret); + } else { + int err = SSL_get_error(ctx->ssl, ret); + + if (SSL_in_init(ctx->ssl) && err == SSL_ERROR_WANT_READ) + goto check_error; + if (!b_data(&ctx->early_buf)) + b_free(&ctx->early_buf); + conn->flags &= ~CO_FL_EARLY_SSL_HS; + break; + } +#endif + +#ifdef SSL_READ_EARLY_DATA_SUCCESS + size_t read_data = 0; ret = SSL_read_early_data(ctx->ssl, b_tail(&ctx->early_buf), b_room(&ctx->early_buf), &read_data); @@ -6073,6 +6094,7 @@ static int ssl_sock_handshake(struct connection *conn, unsigned int flag) TRACE_STATE("Read early data finish", SSL_EV_CONN_HNDSHK, conn, ctx->ssl); break; } +#endif } } #endif @@ -6266,6 +6288,12 @@ check_error: TRACE_ERROR("Zero return error", SSL_EV_CONN_HNDSHK|SSL_EV_CONN_ERR, conn, ctx->ssl, &conn->err_code, &ctx->error_code); goto out_error; +#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC) + } else if (ret == SSL_ERROR_EARLY_DATA_REJECTED) { + conn->err_code = CO_ER_SSL_EARLY_FAILED; + TRACE_ERROR("Early data rejected", SSL_EV_CONN_HNDSHK|SSL_EV_CONN_ERR, conn, ctx->ssl, &conn->err_code); + goto out_error; +#endif } else { /* Fail on all other handshake errors */ @@ -6284,18 +6312,20 @@ check_error: } else { TRACE_STATE("Successful SSL_do_handshake", SSL_EV_CONN_HNDSHK, conn, ctx->ssl); -#ifdef SSL_READ_EARLY_DATA_SUCCESS +#ifdef HAVE_SSL_0RTT /* * If the server refused the early data, we have to send a * 425 to the client, as we no longer have the data to sent * them again. */ if ((conn->flags & CO_FL_EARLY_DATA) && (objt_server(conn->target))) { +#ifdef SSL_READ_EARLY_DATA_SUCCESS if (SSL_get_early_data_status(ctx->ssl) == SSL_EARLY_DATA_REJECTED) { conn->err_code = CO_ER_SSL_EARLY_FAILED; TRACE_ERROR("Early data rejected", SSL_EV_CONN_HNDSHK|SSL_EV_CONN_ERR, conn, ctx->ssl, &conn->err_code); goto out_error; } +#endif } #endif } @@ -6901,7 +6931,7 @@ struct task *ssl_sock_io_cb(struct task *t, void *context, unsigned int state) */ if ((ctx->conn->flags & CO_FL_ERROR) || !(ctx->conn->flags & CO_FL_SSL_WAIT_HS) -#ifdef SSL_READ_EARLY_DATA_SUCCESS +#ifdef HAVE_SSL_0RTT || (b_data(&ctx->early_buf) && (ctx->flags & SSL_SOCK_F_HAS_ALPN || (objt_listener(conn->target) && __objt_listener(conn->target)->bind_conf->mux_proto))) @@ -6937,7 +6967,7 @@ struct task *ssl_sock_io_cb(struct task *t, void *context, unsigned int state) goto leave; } } -#ifdef SSL_READ_EARLY_DATA_SUCCESS +#ifdef HAVE_SSL_0RTT /* If we have early data and somebody wants to receive, let them */ else if (b_data(&ctx->early_buf) && ctx->subs && ctx->subs->events & SUB_RETRY_RECV) { @@ -7000,7 +7030,7 @@ static size_t ssl_sock_to_buf(struct connection *conn, void *xprt_ctx, struct bu BUG_ON_HOT(msg_control != NULL); -#ifdef SSL_READ_EARLY_DATA_SUCCESS +#ifdef HAVE_SSL_0RTT if (b_data(&ctx->early_buf)) { try = b_contig_space(buf); if (try > b_data(&ctx->early_buf))