mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2026-03-14 11:31:59 +01:00
BUG/MINOR: quic: missing app ops init during backend 0-RTT sessions
The QUIC mux requires "application operations" (app ops), which are a list of callbacks associated with the application level (i.e., h3, h0.9) and derived from the ALPN. For 0-RTT, when the session cache cannot be reused before activation, the current code fails to reach the initialization of these app ops, causing the mux to crash during its initialization. To fix this, this patch restores the behavior of ssl_sock_srv_try_reuse_sess(), whose purpose was to reuse sessions stored in the session cache regardless of whether 0-RTT was enabled, prior to this commit: MEDIUM: quic-be: modify ssl_sock_srv_try_reuse_sess() to reuse backend sessions (0-RTT) With this patch, this function now does only one thing: attempt to reuse a session, and that's it! This patch allows ignoring whether a session was successfully reused from the cache or not. This directly fixes the issue where app ops initialization was skipped upon a session cache reuse failure. From a functional standpoint, starting a mux without reusing the session cache has no negative impact; the mux will start, but with no early data to send. Finally, there is the case where the ALPN is reset when the backend is stopped. It is critical to continue locking read access to the ALPN to secure shared access, which this patch does. It is indeed possible for the server to be stopped between the call to connect_server() and quic_reuse_srv_params(). But this cannot prevent the mux to start without app ops. This is why a 'TODO' section was added, as a reminder that a race condition regarding the ALPN reset still needs to be fixed. Must be backported to 3.3
This commit is contained in:
parent
84837b6e70
commit
89c75b0777
@ -73,7 +73,7 @@ int ssl_sock_get_alpn(const struct connection *conn, void *xprt_ctx,
|
||||
const char **str, int *len);
|
||||
int ssl_bio_and_sess_init(struct connection *conn, SSL_CTX *ssl_ctx,
|
||||
SSL **ssl, BIO **bio, BIO_METHOD *bio_meth, void *ctx);
|
||||
int ssl_sock_srv_try_reuse_sess(struct ssl_sock_ctx *ctx, struct server *srv);
|
||||
void ssl_sock_srv_try_reuse_sess(struct ssl_sock_ctx *ctx, struct server *srv);
|
||||
const char *ssl_sock_get_sni(struct connection *conn);
|
||||
const char *ssl_sock_get_cert_sig(struct connection *conn);
|
||||
const char *ssl_sock_get_cipher_name(struct connection *conn);
|
||||
|
||||
@ -285,6 +285,8 @@ int quic_set_app_ops(struct quic_conn *qc, const unsigned char *alpn, size_t alp
|
||||
}
|
||||
|
||||
/* Try to reuse <alpn> ALPN and <etps> early transport parameters.
|
||||
* This function also sets the application operations calling
|
||||
* quic_set_app_ops().
|
||||
* Return 1 if succeeded, 0 if not.
|
||||
*/
|
||||
int quic_reuse_srv_params(struct quic_conn *qc,
|
||||
|
||||
@ -1353,23 +1353,39 @@ int qc_alloc_ssl_sock_ctx(struct quic_conn *qc, void *target)
|
||||
if (!qc_ssl_set_quic_transport_params(ctx->ssl, qc, quic_version_1, 0))
|
||||
goto err;
|
||||
|
||||
if (!(srv->ssl_ctx.options & SRV_SSL_O_EARLY_DATA))
|
||||
ssl_sock_srv_try_reuse_sess(ctx, srv);
|
||||
ssl_sock_srv_try_reuse_sess(ctx, srv);
|
||||
#if (HA_OPENSSL_VERSION_NUMBER >= 0x10101000L) && defined(HAVE_SSL_0RTT_QUIC)
|
||||
else {
|
||||
/* Enable early data only if the SSL session, transport parameters
|
||||
* and application protocol could be reused. This insures the mux is
|
||||
* correctly selected.
|
||||
if ((srv->ssl_ctx.options & SRV_SSL_O_EARLY_DATA)) {
|
||||
int ret;
|
||||
unsigned char *alpn;
|
||||
struct quic_early_transport_params *etps;
|
||||
/* This code is called by connect_server() by way of
|
||||
* conn_prepare().
|
||||
* XXX TODO XXX: there is a remaining race condition where
|
||||
* the negotiated alpn could be resetted before running this code
|
||||
* here. In this case the app_ops for the mux will not be
|
||||
* set by quic_reuse_srv_params().
|
||||
*
|
||||
* Enable the early data only if the transport parameters
|
||||
* and application protocol could be reused. This insures that
|
||||
* no early-data level secrets will be derived if this is not
|
||||
* the case, leading the mux to be started but without being
|
||||
* able to send data at early-data level.
|
||||
*/
|
||||
if (ssl_sock_srv_try_reuse_sess(ctx, srv))
|
||||
HA_RWLOCK_RDLOCK(SERVER_LOCK, &srv->path_params.param_lock);
|
||||
alpn = (unsigned char *)srv->path_params.nego_alpn;
|
||||
etps = &srv->path_params.tps;
|
||||
ret = quic_reuse_srv_params(qc, alpn, etps);
|
||||
HA_RWLOCK_RDUNLOCK(SERVER_LOCK, &srv->path_params.param_lock);
|
||||
if (ret) {
|
||||
SSL_set_quic_early_data_enabled(ctx->ssl, 1);
|
||||
}
|
||||
else {
|
||||
/* No error here. 0-RTT will not be enabled. */
|
||||
TRACE_PROTO("Could not reuse any ALPN", QUIC_EV_CONN_NEW, qc);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
SSL_set_connect_state(ctx->ssl);
|
||||
}
|
||||
|
||||
|
||||
@ -5689,7 +5689,7 @@ int increment_sslconn()
|
||||
* Return 1 if succeeded, 0 if not. Always succeeds for TCP socket. May fail
|
||||
* for QUIC sockets.
|
||||
*/
|
||||
int ssl_sock_srv_try_reuse_sess(struct ssl_sock_ctx *ctx, struct server *srv)
|
||||
void ssl_sock_srv_try_reuse_sess(struct ssl_sock_ctx *ctx, struct server *srv)
|
||||
{
|
||||
#ifdef USE_QUIC
|
||||
struct quic_conn *qc = ctx->qc;
|
||||
@ -5697,11 +5697,9 @@ int ssl_sock_srv_try_reuse_sess(struct ssl_sock_ctx *ctx, struct server *srv)
|
||||
* be set to success(1) only if the QUIC connection parameters
|
||||
* (transport parameters and ALPN) are successfully reused.
|
||||
*/
|
||||
int ret = qc && (srv->ssl_ctx.options & SRV_SSL_O_EARLY_DATA) ? 0 : 1;
|
||||
struct connection *conn = qc ? qc->conn : ctx->conn;
|
||||
#else
|
||||
/* Always succeeds for TCP sockets. */
|
||||
int ret = 1;
|
||||
struct connection *conn = ctx->conn;
|
||||
#endif
|
||||
|
||||
@ -5709,7 +5707,7 @@ int ssl_sock_srv_try_reuse_sess(struct ssl_sock_ctx *ctx, struct server *srv)
|
||||
* Always fail for check connections
|
||||
*/
|
||||
if (conn->flags & CO_FL_SSL_NO_CACHED_INFO)
|
||||
return 0;
|
||||
return;
|
||||
|
||||
HA_RWLOCK_RDLOCK(SSL_SERVER_LOCK, &srv->ssl_ctx.lock);
|
||||
if (srv->ssl_ctx.reused_sess[tid].ptr) {
|
||||
@ -5742,16 +5740,6 @@ int ssl_sock_srv_try_reuse_sess(struct ssl_sock_ctx *ctx, struct server *srv)
|
||||
} else if (sess) {
|
||||
/* already assigned, not needed anymore */
|
||||
SSL_SESSION_free(sess);
|
||||
#ifdef USE_QUIC
|
||||
if (qc && srv->ssl_ctx.options & SRV_SSL_O_EARLY_DATA) {
|
||||
unsigned char *alpn = (unsigned char *)srv->path_params.nego_alpn;
|
||||
struct quic_early_transport_params *etps = &srv->path_params.tps;
|
||||
|
||||
if (quic_reuse_srv_params(qc, alpn, etps))
|
||||
/* Success */
|
||||
ret = 1;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
/* No session available yet, let's see if we can pick one
|
||||
@ -5781,16 +5769,6 @@ int ssl_sock_srv_try_reuse_sess(struct ssl_sock_ctx *ctx, struct server *srv)
|
||||
if (sess) {
|
||||
if (!SSL_set_session(ctx->ssl, sess))
|
||||
HA_ATOMIC_CAS(&srv->ssl_ctx.last_ssl_sess_tid, &old_tid, 0); // no more valid
|
||||
#ifdef USE_QUIC
|
||||
else if (qc && srv->ssl_ctx.options & SRV_SSL_O_EARLY_DATA) {
|
||||
unsigned char *alpn = (unsigned char *)srv->path_params.nego_alpn;
|
||||
struct quic_early_transport_params *etps = &srv->path_params.tps;
|
||||
|
||||
if (quic_reuse_srv_params(qc, alpn, etps))
|
||||
/* Success */
|
||||
ret = 1;
|
||||
}
|
||||
#endif
|
||||
SSL_SESSION_free(sess);
|
||||
}
|
||||
}
|
||||
@ -5800,8 +5778,6 @@ int ssl_sock_srv_try_reuse_sess(struct ssl_sock_ctx *ctx, struct server *srv)
|
||||
}
|
||||
out:
|
||||
HA_RWLOCK_RDUNLOCK(SSL_SERVER_LOCK, &srv->ssl_ctx.lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user