Compare commits
7 Commits
master
...
20250513-q
Author | SHA1 | Date | |
---|---|---|---|
|
4fe959c2c1 | ||
|
86636a5d71 | ||
|
653cb95d54 | ||
|
5075a21fe1 | ||
|
f0c833f334 | ||
|
d96368b1a4 | ||
|
5ec237528d |
31
INSTALL
31
INSTALL
@ -259,10 +259,10 @@ reported to work as well. While there are some efforts from the community to
|
||||
ensure they work well, OpenSSL remains the primary target and this means that
|
||||
in case of conflicting choices, OpenSSL support will be favored over other
|
||||
options. Note that QUIC is not fully supported when haproxy is built with
|
||||
OpenSSL. In this case, QUICTLS is the preferred alternative. As of writing
|
||||
this, the QuicTLS project follows OpenSSL very closely and provides update
|
||||
simultaneously, but being a volunteer-driven project, its long-term future does
|
||||
not look certain enough to convince operating systems to package it, so it
|
||||
OpenSSL < 3.5 version. In this case, QUICTLS is the preferred alternative.
|
||||
As of writing this, the QuicTLS project follows OpenSSL very closely and provides
|
||||
update simultaneously, but being a volunteer-driven project, its long-term future
|
||||
does not look certain enough to convince operating systems to package it, so it
|
||||
needs to be build locally. See the section about QUIC in this document.
|
||||
|
||||
A fifth option is wolfSSL (https://github.com/wolfSSL/wolfssl). It is the only
|
||||
@ -500,10 +500,11 @@ QUIC is the new transport layer protocol and is required for HTTP/3. This
|
||||
protocol stack is currently supported as an experimental feature in haproxy on
|
||||
the frontend side. In order to enable it, use "USE_QUIC=1 USE_OPENSSL=1".
|
||||
|
||||
Note that QUIC is not fully supported by the OpenSSL library. Indeed QUIC 0-RTT
|
||||
cannot be supported by OpenSSL contrary to others libraries with full QUIC
|
||||
support. The preferred option is to use QUICTLS. This is a fork of OpenSSL with
|
||||
a QUIC-compatible API. Its repository is available at this location:
|
||||
Note that QUIC is not always fully supported by the OpenSSL library depending on
|
||||
its version. Indeed QUIC 0-RTT cannot be supported by OpenSSL for versions before
|
||||
3.5 contrary to others libraries with full QUIC support. The preferred option is
|
||||
to use QUICTLS. This is a fork of OpenSSL with a QUIC-compatible API. Its
|
||||
repository is available at this location:
|
||||
|
||||
https://github.com/quictls/openssl
|
||||
|
||||
@ -531,14 +532,18 @@ way assuming that wolfSSL was installed in /opt/wolfssl-5.6.0 as shown in 4.5:
|
||||
SSL_INC=/opt/wolfssl-5.6.0/include SSL_LIB=/opt/wolfssl-5.6.0/lib
|
||||
LDFLAGS="-Wl,-rpath,/opt/wolfssl-5.6.0/lib"
|
||||
|
||||
As last resort, haproxy may be compiled against OpenSSL as follows:
|
||||
As last resort, haproxy may be compiled against OpenSSL as follows from 3.5
|
||||
version with 0-RTT support:
|
||||
|
||||
$ make TARGET=generic USE_OPENSSL=1 USE_QUIC=1
|
||||
|
||||
or as follows for all OpenSSL versions but without O-RTT support:
|
||||
|
||||
$ make TARGET=generic USE_OPENSSL=1 USE_QUIC=1 USE_QUIC_OPENSSL_COMPAT=1
|
||||
|
||||
Note that QUIC 0-RTT is not supported by haproxy QUIC stack when built against
|
||||
OpenSSL. In addition to this compilation requirements, the QUIC listener
|
||||
bindings must be explicitly enabled with a specific QUIC tuning parameter.
|
||||
(see "limited-quic" global parameter of haproxy Configuration Manual).
|
||||
In addition to this requirements, the QUIC listener bindings must be explicitly
|
||||
enabled with a specific QUIC tuning parameter. (see "limited-quic" global
|
||||
parameter of haproxy Configuration Manual).
|
||||
|
||||
|
||||
5) How to build HAProxy
|
||||
|
@ -46,7 +46,25 @@
|
||||
|
||||
#ifdef USE_QUIC_OPENSSL_COMPAT
|
||||
#include <haproxy/quic_openssl_compat.h>
|
||||
#else
|
||||
#if defined(OSSL_FUNC_SSL_QUIC_TLS_CRYPTO_SEND)
|
||||
/* This macro is defined by the new OpenSSL 3.5.0 QUIC TLS API and it is not
|
||||
* defined by quictls.
|
||||
*/
|
||||
#define HAVE_OPENSSL_QUIC
|
||||
#define SSL_set_quic_transport_params SSL_set_quic_tls_transport_params
|
||||
#define SSL_set_quic_early_data_enabled SSL_set_quic_tls_early_data_enabled
|
||||
#define SSL_quic_read_level(arg) -1
|
||||
|
||||
enum ssl_encryption_level_t {
|
||||
ssl_encryption_initial = 0,
|
||||
ssl_encryption_early_data,
|
||||
ssl_encryption_handshake,
|
||||
ssl_encryption_application
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif /* USE_QUIC_OPENSSL_COMPAT */
|
||||
|
||||
#if defined(OPENSSL_IS_AWSLC)
|
||||
#define OPENSSL_NO_DH
|
||||
|
@ -334,7 +334,7 @@ struct quic_conn {
|
||||
int tps_tls_ext;
|
||||
int state;
|
||||
enum qc_mux_state mux_state; /* status of the connection/mux layer */
|
||||
#ifdef USE_QUIC_OPENSSL_COMPAT
|
||||
#if defined(USE_QUIC_OPENSSL_COMPAT) || defined(HAVE_OPENSSL_QUIC)
|
||||
unsigned char enc_params[QUIC_TP_MAX_ENCLEN]; /* encoded QUIC transport parameters */
|
||||
size_t enc_params_len;
|
||||
#endif
|
||||
@ -345,6 +345,9 @@ struct quic_conn {
|
||||
*/
|
||||
uint64_t hash64;
|
||||
|
||||
#ifdef HAVE_OPENSSL_QUIC
|
||||
uint32_t prot_level;
|
||||
#endif
|
||||
/* Initial encryption level */
|
||||
struct quic_enc_level *iel;
|
||||
/* 0-RTT encryption level */
|
||||
|
@ -36,6 +36,7 @@
|
||||
int ssl_quic_initial_ctx(struct bind_conf *bind_conf);
|
||||
int qc_alloc_ssl_sock_ctx(struct quic_conn *qc);
|
||||
int qc_ssl_provide_all_quic_data(struct quic_conn *qc, struct ssl_sock_ctx *ctx);
|
||||
int quic_ssl_set_tls_cbs(SSL *ssl);
|
||||
|
||||
static inline void qc_free_ssl_sock_ctx(struct ssl_sock_ctx **ctx)
|
||||
{
|
||||
|
@ -291,6 +291,29 @@ static inline struct quic_enc_level **ssl_to_qel_addr(struct quic_conn *qc,
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_OPENSSL_QUIC
|
||||
/* Simple helper function which translate an OpenSSL SSL protection level
|
||||
* to a quictls SSL encryption. This way the code which use the OpenSSL QUIC API
|
||||
* may use the code which uses the quictls API.
|
||||
*/
|
||||
static inline enum ssl_encryption_level_t ssl_prot_level_to_enc_level(struct quic_conn *qc,
|
||||
uint32_t prot_level)
|
||||
{
|
||||
switch (prot_level) {
|
||||
case OSSL_RECORD_PROTECTION_LEVEL_NONE:
|
||||
return ssl_encryption_initial;
|
||||
case OSSL_RECORD_PROTECTION_LEVEL_EARLY:
|
||||
return ssl_encryption_early_data;
|
||||
case OSSL_RECORD_PROTECTION_LEVEL_HANDSHAKE:
|
||||
return ssl_encryption_handshake;
|
||||
case OSSL_RECORD_PROTECTION_LEVEL_APPLICATION:
|
||||
return ssl_encryption_application;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Return the address of the QUIC TLS encryption level associated to <level> internal
|
||||
* encryption level and attached to <qc> QUIC connection if succeeded, or
|
||||
* NULL if failed.
|
||||
|
@ -99,5 +99,6 @@ struct quic_rx_crypto_frm {
|
||||
#define QUIC_EV_CONN_KP (1ULL << 50)
|
||||
#define QUIC_EV_CONN_SSL_COMPAT (1ULL << 51)
|
||||
#define QUIC_EV_CONN_BIND_TID (1ULL << 52)
|
||||
#define QUIC_EV_CONN_RELEASE_RCD (1ULL << 53)
|
||||
|
||||
#endif /* _HAPROXY_QUIC_TRACE_T_H */
|
||||
|
@ -1109,6 +1109,9 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
|
||||
quic_tls_ku_reset(&qc->ku.nxt_rx);
|
||||
quic_tls_ku_reset(&qc->ku.nxt_tx);
|
||||
|
||||
#ifdef HAVE_OPENSSL_QUIC
|
||||
qc->prot_level = OSSL_RECORD_PROTECTION_LEVEL_NONE;
|
||||
#endif
|
||||
/* Encryption levels */
|
||||
qc->iel = qc->eel = qc->hel = qc->ael = NULL;
|
||||
LIST_INIT(&qc->qel_list);
|
||||
|
370
src/quic_ssl.c
370
src/quic_ssl.c
@ -10,8 +10,6 @@
|
||||
#include <haproxy/ssl_sock.h>
|
||||
#include <haproxy/trace.h>
|
||||
|
||||
static BIO_METHOD *ha_quic_meth;
|
||||
|
||||
DECLARE_POOL(pool_head_quic_ssl_sock_ctx, "quic_ssl_sock_ctx", sizeof(struct ssl_sock_ctx));
|
||||
|
||||
/* Set the encoded version of the transport parameter into the TLS
|
||||
@ -23,7 +21,7 @@ static int qc_ssl_set_quic_transport_params(struct quic_conn *qc,
|
||||
const struct quic_version *ver, int server)
|
||||
{
|
||||
int ret = 0;
|
||||
#ifdef USE_QUIC_OPENSSL_COMPAT
|
||||
#if defined(USE_QUIC_OPENSSL_COMPAT) || defined(HAVE_OPENSSL_QUIC)
|
||||
unsigned char *in = qc->enc_params;
|
||||
size_t insz = sizeof qc->enc_params;
|
||||
size_t *enclen = &qc->enc_params_len;
|
||||
@ -327,27 +325,6 @@ write:
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if defined(OPENSSL_IS_AWSLC)
|
||||
/* compatibility function for split read/write encryption secrets to be used
|
||||
* with the API which uses 2 callbacks. */
|
||||
static inline int ha_quic_set_read_secret(SSL *ssl, enum ssl_encryption_level_t level,
|
||||
const SSL_CIPHER *cipher, const uint8_t *secret,
|
||||
size_t secret_len)
|
||||
{
|
||||
return ha_quic_set_encryption_secrets(ssl, level, secret, NULL, secret_len);
|
||||
|
||||
}
|
||||
|
||||
static inline int ha_quic_set_write_secret(SSL *ssl, enum ssl_encryption_level_t level,
|
||||
const SSL_CIPHER *cipher, const uint8_t *secret,
|
||||
size_t secret_len)
|
||||
{
|
||||
|
||||
return ha_quic_set_encryption_secrets(ssl, level, NULL, secret, secret_len);
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
/* ->add_handshake_data QUIC TLS callback used by the QUIC TLS stack when it
|
||||
* wants to provide the QUIC layer with CRYPTO data.
|
||||
* Returns 1 if succeeded, 0 if not.
|
||||
@ -391,6 +368,291 @@ static int ha_quic_add_handshake_data(SSL *ssl, enum ssl_encryption_level_t leve
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef HAVE_OPENSSL_QUIC
|
||||
/************************** OpenSSL QUIC TLS API (>= 3.5.0) *******************/
|
||||
|
||||
/* Callback called by OpenSSL when it needs to send CRYPTO data to the peer.
|
||||
* This is done from <buf> buffer with <buf_len> as number of bytes to be sent.
|
||||
* This callback must set <*consumed> to the number of bytes which could be
|
||||
* consumed (buffered in our case) before being sent to the peer. This is always
|
||||
* <buf_len> when this callback succeeds, or 0 when it fails.
|
||||
* Return 1 if succeeded, 0 if not.
|
||||
*/
|
||||
static int ha_quic_ossl_crypto_send(SSL *ssl,
|
||||
const unsigned char *buf, size_t buf_len,
|
||||
size_t *consumed, void *arg)
|
||||
{
|
||||
int ret = 0;
|
||||
struct quic_conn *qc = SSL_get_ex_data(ssl, ssl_qc_app_data_index);
|
||||
enum ssl_encryption_level_t level = ssl_prot_level_to_enc_level(qc, qc->prot_level);
|
||||
|
||||
TRACE_ENTER(QUIC_EV_CONN_ADDDATA, qc);
|
||||
|
||||
if (!ha_quic_add_handshake_data(ssl, level, buf, buf_len))
|
||||
goto err;
|
||||
|
||||
*consumed = buf_len;
|
||||
TRACE_DEVEL("CRYPTO data buffered", QUIC_EV_CONN_ADDDATA, qc, &level, &buf_len);
|
||||
|
||||
ret = 1;
|
||||
leave:
|
||||
TRACE_LEAVE(QUIC_EV_CONN_ADDDATA, qc);
|
||||
return ret;
|
||||
err:
|
||||
*consumed = 0;
|
||||
TRACE_DEVEL("leaving on error", QUIC_EV_CONN_ADDDATA, qc);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
/* Callback to provide CRYPTO data from the peer to the TLS stack. It must set
|
||||
* <buf> to the address of the buffer which contains the CRYPTO data.
|
||||
* <*byte_read> value must be the number of bytes of CRYPTO data received.
|
||||
* Never fail, always return 1.
|
||||
*/
|
||||
static int ha_quic_ossl_crypto_recv_rcd(SSL *ssl,
|
||||
const unsigned char **buf,
|
||||
size_t *bytes_read,
|
||||
void *arg)
|
||||
{
|
||||
struct quic_conn *qc = SSL_get_ex_data(ssl, ssl_qc_app_data_index);
|
||||
struct quic_enc_level *qel;
|
||||
struct ncbuf *ncbuf = NULL;
|
||||
struct quic_cstream *cstream = NULL;
|
||||
ncb_sz_t data = 0;
|
||||
|
||||
TRACE_ENTER(QUIC_EV_CONN_SSLDATA, qc);
|
||||
|
||||
list_for_each_entry(qel, &qc->qel_list, list) {
|
||||
cstream = qel->cstream;
|
||||
if (!cstream)
|
||||
continue;
|
||||
|
||||
ncbuf = &cstream->rx.ncbuf;
|
||||
if (ncb_is_null(ncbuf))
|
||||
continue;
|
||||
|
||||
data = ncb_data(ncbuf, 0);
|
||||
if (data)
|
||||
break;
|
||||
}
|
||||
|
||||
if (data) {
|
||||
const unsigned char *cdata;
|
||||
|
||||
BUG_ON(ncb_is_null(ncbuf) || !cstream);
|
||||
/* <ncbuf> must not be released at this time. */
|
||||
cdata = (const unsigned char *)ncb_head(ncbuf);
|
||||
cstream->rx.offset += data;
|
||||
TRACE_DEVEL("buffered crypto data were provided to TLS stack",
|
||||
QUIC_EV_CONN_PHPKTS, qc, qel);
|
||||
*buf = cdata;
|
||||
*bytes_read = data;
|
||||
}
|
||||
else {
|
||||
*buf = NULL;
|
||||
*bytes_read = 0;
|
||||
}
|
||||
|
||||
TRACE_LEAVE(QUIC_EV_CONN_SSLDATA, qc);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Callback to release the CRYPT data buffer which have been received
|
||||
* by ha_quic_ossl_crypto_recv_rcd().
|
||||
* Return 0 if failed, this means no buffer could be released, or 1 if
|
||||
* succeeded.
|
||||
*/
|
||||
static int ha_quic_ossl_crypto_release_rcd(SSL *ssl,
|
||||
size_t bytes_read, void *arg)
|
||||
{
|
||||
int ret = 0;
|
||||
struct quic_conn *qc = SSL_get_ex_data(ssl, ssl_qc_app_data_index);
|
||||
struct quic_enc_level *qel;
|
||||
|
||||
TRACE_ENTER(QUIC_EV_CONN_RELEASE_RCD, qc);
|
||||
|
||||
list_for_each_entry(qel, &qc->qel_list, list) {
|
||||
struct quic_cstream *cstream = qel->cstream;
|
||||
struct ncbuf *ncbuf;
|
||||
ncb_sz_t data;
|
||||
|
||||
if (!cstream)
|
||||
continue;
|
||||
|
||||
ncbuf = &cstream->rx.ncbuf;
|
||||
if (ncb_is_null(ncbuf))
|
||||
continue;
|
||||
|
||||
data = ncb_data(ncbuf, 0);
|
||||
if (!data)
|
||||
continue;
|
||||
|
||||
data = data > bytes_read ? bytes_read : data;
|
||||
ncb_advance(ncbuf, data);
|
||||
bytes_read -= data;
|
||||
if (ncb_is_empty(ncbuf)) {
|
||||
TRACE_DEVEL("freeing crypto buf", QUIC_EV_CONN_PHPKTS, qc, qel);
|
||||
quic_free_ncbuf(ncbuf);
|
||||
}
|
||||
|
||||
ret = 1;
|
||||
if (bytes_read == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!ret)
|
||||
goto err;
|
||||
leave:
|
||||
TRACE_LEAVE(QUIC_EV_CONN_RELEASE_RCD, qc);
|
||||
return ret;
|
||||
err:
|
||||
TRACE_DEVEL("leaving on error", QUIC_EV_CONN_RELEASE_RCD, qc);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
/* Callback called by OpenSSL when <secret> has been established at
|
||||
* <prot_level> SSL protection level. <direction> value is 0 for a read secret,
|
||||
* 1 for a write secret.
|
||||
* Return 1 if succeeded, 0 if not.
|
||||
*/
|
||||
static int ha_quic_ossl_yield_secret(SSL *ssl, uint32_t prot_level, int direction,
|
||||
const unsigned char *secret, size_t secret_len,
|
||||
void *arg)
|
||||
{
|
||||
int ret = 0;
|
||||
struct quic_conn *qc = SSL_get_ex_data(ssl, ssl_qc_app_data_index);
|
||||
enum ssl_encryption_level_t level = ssl_prot_level_to_enc_level(qc, prot_level);
|
||||
|
||||
TRACE_ENTER(QUIC_EV_CONN_RWSEC, qc);
|
||||
|
||||
BUG_ON(level == -1);
|
||||
|
||||
if (!direction) {
|
||||
/* read secret */
|
||||
if (!ha_quic_set_encryption_secrets(ssl, level, secret, NULL, secret_len))
|
||||
goto err;
|
||||
}
|
||||
else {
|
||||
/* write secret */
|
||||
if (!ha_quic_set_encryption_secrets(ssl, level, NULL, secret, secret_len))
|
||||
goto err;
|
||||
|
||||
qc->prot_level = prot_level;
|
||||
}
|
||||
|
||||
ret = 1;
|
||||
leave:
|
||||
TRACE_LEAVE(QUIC_EV_CONN_RWSEC, qc);
|
||||
return ret;
|
||||
err:
|
||||
TRACE_DEVEL("leaving on error", QUIC_EV_CONN_RWSEC, qc);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
/* Callback called by OpenSSL when the peer transport parameters have been
|
||||
* received.
|
||||
* Return 1 if succeeded, 0 if not.
|
||||
*/
|
||||
static int ha_quic_ossl_got_transport_params(SSL *ssl, const unsigned char *params,
|
||||
size_t params_len, void *arg)
|
||||
{
|
||||
int ret = 0;
|
||||
struct quic_conn *qc = SSL_get_ex_data(ssl, ssl_qc_app_data_index);
|
||||
const struct quic_version *ver =
|
||||
qc->negotiated_version ? qc->negotiated_version : qc->original_version;
|
||||
|
||||
TRACE_ENTER(QUIC_EV_TRANSP_PARAMS, qc);
|
||||
|
||||
if (qc->flags & QUIC_FL_CONN_TX_TP_RECEIVED) {
|
||||
TRACE_PROTO("peer transport parameters already received",
|
||||
QUIC_EV_TRANSP_PARAMS, qc);
|
||||
ret = 1;
|
||||
}
|
||||
else {
|
||||
if (!quic_transport_params_store(qc, 0, params, params + params_len) ||
|
||||
!qc_ssl_set_quic_transport_params(qc, ver, 1))
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = 1;
|
||||
leave:
|
||||
TRACE_LEAVE(QUIC_EV_TRANSP_PARAMS, qc);
|
||||
return ret;
|
||||
err:
|
||||
TRACE_DEVEL("leaving on error", QUIC_EV_CONN_RWSEC, qc);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
/* Callback called by OpenSSL when it needs to send a TLS to the peer with
|
||||
* <alert_code> as value.
|
||||
* Always succeeds.
|
||||
*/
|
||||
static int ha_quic_ossl_alert(SSL *ssl, unsigned char alert_code, void *arg)
|
||||
{
|
||||
int ret = 1, alert = alert_code;
|
||||
struct quic_conn *qc = SSL_get_ex_data(ssl, ssl_qc_app_data_index);
|
||||
|
||||
TRACE_ENTER(QUIC_EV_CONN_SSLALERT, qc);
|
||||
|
||||
TRACE_PROTO("Received TLS alert", QUIC_EV_CONN_SSLALERT, qc, &alert);
|
||||
quic_set_tls_alert(qc, alert_code);
|
||||
|
||||
TRACE_LEAVE(QUIC_EV_CONN_SSLALERT, qc);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const OSSL_DISPATCH ha_quic_dispatch[] = {
|
||||
{
|
||||
OSSL_FUNC_SSL_QUIC_TLS_CRYPTO_SEND,
|
||||
(OSSL_FUNC)ha_quic_ossl_crypto_send,
|
||||
},
|
||||
{
|
||||
OSSL_FUNC_SSL_QUIC_TLS_CRYPTO_RECV_RCD,
|
||||
(OSSL_FUNC)ha_quic_ossl_crypto_recv_rcd,
|
||||
},
|
||||
{
|
||||
OSSL_FUNC_SSL_QUIC_TLS_CRYPTO_RELEASE_RCD,
|
||||
(OSSL_FUNC)ha_quic_ossl_crypto_release_rcd,
|
||||
},
|
||||
{
|
||||
OSSL_FUNC_SSL_QUIC_TLS_YIELD_SECRET,
|
||||
(OSSL_FUNC)ha_quic_ossl_yield_secret,
|
||||
},
|
||||
{
|
||||
OSSL_FUNC_SSL_QUIC_TLS_GOT_TRANSPORT_PARAMS,
|
||||
(OSSL_FUNC)ha_quic_ossl_got_transport_params,
|
||||
},
|
||||
{
|
||||
OSSL_FUNC_SSL_QUIC_TLS_ALERT,
|
||||
(OSSL_FUNC)ha_quic_ossl_alert,
|
||||
},
|
||||
OSSL_DISPATCH_END,
|
||||
};
|
||||
#else /* !HAVE_OPENSSL_QUIC */
|
||||
/***************************** QUICTLS QUIC API ******************************/
|
||||
|
||||
#if defined(OPENSSL_IS_AWSLC)
|
||||
/* compatibility function for split read/write encryption secrets to be used
|
||||
* with the API which uses 2 callbacks. */
|
||||
static inline int ha_quic_set_read_secret(SSL *ssl, enum ssl_encryption_level_t level,
|
||||
const SSL_CIPHER *cipher, const uint8_t *secret,
|
||||
size_t secret_len)
|
||||
{
|
||||
return ha_quic_set_encryption_secrets(ssl, level, secret, NULL, secret_len);
|
||||
|
||||
}
|
||||
|
||||
static inline int ha_quic_set_write_secret(SSL *ssl, enum ssl_encryption_level_t level,
|
||||
const SSL_CIPHER *cipher, const uint8_t *secret,
|
||||
size_t secret_len)
|
||||
{
|
||||
|
||||
return ha_quic_set_encryption_secrets(ssl, level, NULL, secret, secret_len);
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
static int ha_quic_flush_flight(SSL *ssl)
|
||||
{
|
||||
struct quic_conn *qc = SSL_get_ex_data(ssl, ssl_qc_app_data_index);
|
||||
@ -434,6 +696,7 @@ static SSL_QUIC_METHOD ha_quic_method = {
|
||||
.send_alert = ha_quic_send_alert,
|
||||
};
|
||||
#endif
|
||||
#endif /* HAVE_OPENSSL_QUIC */
|
||||
|
||||
/* Initialize the TLS context of a listener with <bind_conf> as configuration.
|
||||
* Returns an error count.
|
||||
@ -535,11 +798,13 @@ static int qc_ssl_provide_quic_data(struct ncbuf *ncbuf,
|
||||
|
||||
TRACE_ENTER(QUIC_EV_CONN_SSLDATA, qc);
|
||||
|
||||
#ifndef HAVE_OPENSSL_QUIC
|
||||
if (SSL_provide_quic_data(ctx->ssl, level, data, len) != 1) {
|
||||
TRACE_ERROR("SSL_provide_quic_data() error",
|
||||
QUIC_EV_CONN_SSLDATA, qc, NULL, NULL, ctx->ssl);
|
||||
goto leave;
|
||||
}
|
||||
#endif
|
||||
|
||||
state = qc->state;
|
||||
if (state < QUIC_HS_ST_COMPLETE) {
|
||||
@ -609,7 +874,27 @@ static int qc_ssl_provide_quic_data(struct ncbuf *ncbuf,
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_OPENSSL_QUIC
|
||||
TRACE_PROTO("SSL handshake OK", QUIC_EV_CONN_IO_CB, qc, &state);
|
||||
#else
|
||||
/* Hack to support O-RTT with the OpenSSL 3.5 QUIC API.
|
||||
* SSL_do_handshake() succeeds at the first call. Why? |-(
|
||||
* This prevents the handshake CRYPTO data to be sent.
|
||||
* To overcome this, ensure one does not consider the handshake is
|
||||
* successful if the read application level secrets have not been
|
||||
* provided by the stack. This happens after having received the peer
|
||||
* handshake level CRYPTO data which are validated by the TLS stack.
|
||||
*/
|
||||
if (qc->li->bind_conf->ssl_conf.early_data &&
|
||||
(!qc->ael || !qc->ael->tls_ctx.rx.secret)) {
|
||||
TRACE_PROTO("SSL handshake in progress",
|
||||
QUIC_EV_CONN_IO_CB, qc, &state, &ssl_err);
|
||||
goto out;
|
||||
}
|
||||
else {
|
||||
TRACE_PROTO("SSL handshake OK", QUIC_EV_CONN_IO_CB, qc, &state);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Check the alpn could be negotiated */
|
||||
if (!qc->app_ops) {
|
||||
@ -647,7 +932,9 @@ static int qc_ssl_provide_quic_data(struct ncbuf *ncbuf,
|
||||
TRACE_ERROR("quic_tls_key_update() failed", QUIC_EV_CONN_IO_CB, qc);
|
||||
goto leave;
|
||||
}
|
||||
} else {
|
||||
}
|
||||
#ifndef HAVE_OPENSSL_QUIC
|
||||
else {
|
||||
ssl_err = SSL_process_quic_post_handshake(ctx->ssl);
|
||||
if (ssl_err != 1) {
|
||||
ssl_err = SSL_get_error(ctx->ssl, ssl_err);
|
||||
@ -664,6 +951,7 @@ static int qc_ssl_provide_quic_data(struct ncbuf *ncbuf,
|
||||
|
||||
TRACE_STATE("SSL post handshake succeeded", QUIC_EV_CONN_IO_CB, qc, &state);
|
||||
}
|
||||
#endif
|
||||
|
||||
out:
|
||||
ret = 1;
|
||||
@ -735,6 +1023,16 @@ int qc_ssl_provide_all_quic_data(struct quic_conn *qc, struct ssl_sock_ctx *ctx)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Simple helper to set the specifig OpenSSL/quictls QUIC API callbacks */
|
||||
int quic_ssl_set_tls_cbs(SSL *ssl)
|
||||
{
|
||||
#ifdef HAVE_OPENSSL_QUIC
|
||||
return SSL_set_quic_tls_cbs(ssl, ha_quic_dispatch, NULL);
|
||||
#else
|
||||
return SSL_set_quic_method(ssl, &ha_quic_method);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Try to allocate the <*ssl> SSL session object for <qc> QUIC connection
|
||||
* with <ssl_ctx> as SSL context inherited settings. Also set the transport
|
||||
* parameters of this session.
|
||||
@ -754,18 +1052,18 @@ static int qc_ssl_sess_init(struct quic_conn *qc, SSL_CTX *ssl_ctx, SSL **ssl)
|
||||
*ssl = SSL_new(ssl_ctx);
|
||||
if (!*ssl) {
|
||||
if (!retry--)
|
||||
goto leave;
|
||||
goto err;
|
||||
|
||||
pool_gc(NULL);
|
||||
goto retry;
|
||||
}
|
||||
|
||||
if (!SSL_set_ex_data(*ssl, ssl_qc_app_data_index, qc) ||
|
||||
!SSL_set_quic_method(*ssl, &ha_quic_method)) {
|
||||
!quic_ssl_set_tls_cbs(*ssl)) {
|
||||
SSL_free(*ssl);
|
||||
*ssl = NULL;
|
||||
if (!retry--)
|
||||
goto leave;
|
||||
goto err;
|
||||
|
||||
pool_gc(NULL);
|
||||
goto retry;
|
||||
@ -775,6 +1073,9 @@ static int qc_ssl_sess_init(struct quic_conn *qc, SSL_CTX *ssl_ctx, SSL **ssl)
|
||||
leave:
|
||||
TRACE_LEAVE(QUIC_EV_CONN_NEW, qc);
|
||||
return ret;
|
||||
err:
|
||||
TRACE_DEVEL("leaving on error", QUIC_EV_CONN_NEW, qc);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
#ifdef HAVE_SSL_0RTT_QUIC
|
||||
@ -872,18 +1173,7 @@ int qc_alloc_ssl_sock_ctx(struct quic_conn *qc)
|
||||
return !ret;
|
||||
|
||||
err:
|
||||
TRACE_DEVEL("leaving on error", QUIC_EV_CONN_NEW, qc);
|
||||
pool_free(pool_head_quic_ssl_sock_ctx, ctx);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
static void __quic_conn_init(void)
|
||||
{
|
||||
ha_quic_meth = BIO_meth_new(0x666, "ha QUIC methods");
|
||||
}
|
||||
INITCALL0(STG_REGISTER, __quic_conn_init);
|
||||
|
||||
static void __quic_conn_deinit(void)
|
||||
{
|
||||
BIO_meth_free(ha_quic_meth);
|
||||
}
|
||||
REGISTER_POST_DEINIT(__quic_conn_deinit);
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <haproxy/proto_tcp.h>
|
||||
#include <haproxy/quic_conn.h>
|
||||
#include <haproxy/quic_openssl_compat.h>
|
||||
#include <haproxy/quic_ssl.h>
|
||||
#include <haproxy/quic_tp.h>
|
||||
#include <haproxy/ssl_ckch.h>
|
||||
#include <haproxy/ssl_gencert.h>
|
||||
@ -28,6 +29,9 @@ static void ssl_sock_switchctx_set(SSL *ssl, SSL_CTX *ctx)
|
||||
SSL_set_verify(ssl, SSL_CTX_get_verify_mode(ctx), ssl_sock_bind_verifycbk);
|
||||
SSL_set_client_CA_list(ssl, SSL_dup_CA_list(SSL_CTX_get_client_CA_list(ctx)));
|
||||
SSL_set_SSL_CTX(ssl, ctx);
|
||||
#if defined(USE_QUIC) && defined(HAVE_OPENSSL_QUIC)
|
||||
quic_ssl_set_tls_cbs(ssl);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -12,6 +12,7 @@
|
||||
|
||||
#include <haproxy/errors.h>
|
||||
#include <haproxy/openssl-compat.h>
|
||||
#include <haproxy/quic_ssl.h>
|
||||
#include <haproxy/ssl_ckch.h>
|
||||
#include <haproxy/ssl_sock.h>
|
||||
#include <haproxy/xxhash.h>
|
||||
@ -284,8 +285,12 @@ SSL_CTX *ssl_sock_assign_generated_cert(unsigned int key, struct bind_conf *bind
|
||||
HA_RWLOCK_WRLOCK(SSL_GEN_CERTS_LOCK, &ssl_ctx_lru_rwlock);
|
||||
lru = lru64_lookup(key, ssl_ctx_lru_tree, bind_conf->ca_sign_ckch->cert, 0);
|
||||
if (lru && lru->domain) {
|
||||
if (ssl)
|
||||
if (ssl) {
|
||||
SSL_set_SSL_CTX(ssl, (SSL_CTX *)lru->data);
|
||||
#if defined(USE_QUIC) && defined(HAVE_OPENSSL_QUIC)
|
||||
quic_ssl_set_tls_cbs(ssl);
|
||||
#endif
|
||||
}
|
||||
HA_RWLOCK_WRUNLOCK(SSL_GEN_CERTS_LOCK, &ssl_ctx_lru_rwlock);
|
||||
return (SSL_CTX *)lru->data;
|
||||
}
|
||||
@ -354,12 +359,18 @@ int ssl_sock_generate_certificate(const char *servername, struct bind_conf *bind
|
||||
lru64_commit(lru, ssl_ctx, cacert, 0, (void (*)(void *))SSL_CTX_free);
|
||||
}
|
||||
SSL_set_SSL_CTX(ssl, ssl_ctx);
|
||||
#if defined(USE_QUIC) && defined(HAVE_OPENSSL_QUIC)
|
||||
quic_ssl_set_tls_cbs(ssl);
|
||||
#endif
|
||||
HA_RWLOCK_WRUNLOCK(SSL_GEN_CERTS_LOCK, &ssl_ctx_lru_rwlock);
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
ssl_ctx = ssl_sock_do_create_cert(servername, bind_conf, ssl);
|
||||
SSL_set_SSL_CTX(ssl, ssl_ctx);
|
||||
#if defined(USE_QUIC) && defined(HAVE_OPENSSL_QUIC)
|
||||
quic_ssl_set_tls_cbs(ssl);
|
||||
#endif
|
||||
/* No LRU cache, this CTX will be released as soon as the session dies */
|
||||
SSL_CTX_free(ssl_ctx);
|
||||
return 1;
|
||||
|
Loading…
x
Reference in New Issue
Block a user