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
|
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
|
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
|
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
|
OpenSSL < 3.5 version. In this case, QUICTLS is the preferred alternative.
|
||||||
this, the QuicTLS project follows OpenSSL very closely and provides update
|
As of writing this, the QuicTLS project follows OpenSSL very closely and provides
|
||||||
simultaneously, but being a volunteer-driven project, its long-term future does
|
update simultaneously, but being a volunteer-driven project, its long-term future
|
||||||
not look certain enough to convince operating systems to package it, so it
|
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.
|
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
|
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
|
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".
|
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
|
Note that QUIC is not always fully supported by the OpenSSL library depending on
|
||||||
cannot be supported by OpenSSL contrary to others libraries with full QUIC
|
its version. Indeed QUIC 0-RTT cannot be supported by OpenSSL for versions before
|
||||||
support. The preferred option is to use QUICTLS. This is a fork of OpenSSL with
|
3.5 contrary to others libraries with full QUIC support. The preferred option is
|
||||||
a QUIC-compatible API. Its repository is available at this location:
|
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
|
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
|
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"
|
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
|
$ 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
|
In addition to this requirements, the QUIC listener bindings must be explicitly
|
||||||
OpenSSL. In addition to this compilation requirements, the QUIC listener
|
enabled with a specific QUIC tuning parameter. (see "limited-quic" global
|
||||||
bindings must be explicitly enabled with a specific QUIC tuning parameter.
|
parameter of haproxy Configuration Manual).
|
||||||
(see "limited-quic" global parameter of haproxy Configuration Manual).
|
|
||||||
|
|
||||||
|
|
||||||
5) How to build HAProxy
|
5) How to build HAProxy
|
||||||
|
@ -46,7 +46,25 @@
|
|||||||
|
|
||||||
#ifdef USE_QUIC_OPENSSL_COMPAT
|
#ifdef USE_QUIC_OPENSSL_COMPAT
|
||||||
#include <haproxy/quic_openssl_compat.h>
|
#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
|
||||||
|
#endif /* USE_QUIC_OPENSSL_COMPAT */
|
||||||
|
|
||||||
#if defined(OPENSSL_IS_AWSLC)
|
#if defined(OPENSSL_IS_AWSLC)
|
||||||
#define OPENSSL_NO_DH
|
#define OPENSSL_NO_DH
|
||||||
|
@ -334,7 +334,7 @@ struct quic_conn {
|
|||||||
int tps_tls_ext;
|
int tps_tls_ext;
|
||||||
int state;
|
int state;
|
||||||
enum qc_mux_state mux_state; /* status of the connection/mux layer */
|
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 */
|
unsigned char enc_params[QUIC_TP_MAX_ENCLEN]; /* encoded QUIC transport parameters */
|
||||||
size_t enc_params_len;
|
size_t enc_params_len;
|
||||||
#endif
|
#endif
|
||||||
@ -345,6 +345,9 @@ struct quic_conn {
|
|||||||
*/
|
*/
|
||||||
uint64_t hash64;
|
uint64_t hash64;
|
||||||
|
|
||||||
|
#ifdef HAVE_OPENSSL_QUIC
|
||||||
|
uint32_t prot_level;
|
||||||
|
#endif
|
||||||
/* Initial encryption level */
|
/* Initial encryption level */
|
||||||
struct quic_enc_level *iel;
|
struct quic_enc_level *iel;
|
||||||
/* 0-RTT encryption level */
|
/* 0-RTT encryption level */
|
||||||
|
@ -36,6 +36,7 @@
|
|||||||
int ssl_quic_initial_ctx(struct bind_conf *bind_conf);
|
int ssl_quic_initial_ctx(struct bind_conf *bind_conf);
|
||||||
int qc_alloc_ssl_sock_ctx(struct quic_conn *qc);
|
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 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)
|
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
|
/* Return the address of the QUIC TLS encryption level associated to <level> internal
|
||||||
* encryption level and attached to <qc> QUIC connection if succeeded, or
|
* encryption level and attached to <qc> QUIC connection if succeeded, or
|
||||||
* NULL if failed.
|
* NULL if failed.
|
||||||
|
@ -99,5 +99,6 @@ struct quic_rx_crypto_frm {
|
|||||||
#define QUIC_EV_CONN_KP (1ULL << 50)
|
#define QUIC_EV_CONN_KP (1ULL << 50)
|
||||||
#define QUIC_EV_CONN_SSL_COMPAT (1ULL << 51)
|
#define QUIC_EV_CONN_SSL_COMPAT (1ULL << 51)
|
||||||
#define QUIC_EV_CONN_BIND_TID (1ULL << 52)
|
#define QUIC_EV_CONN_BIND_TID (1ULL << 52)
|
||||||
|
#define QUIC_EV_CONN_RELEASE_RCD (1ULL << 53)
|
||||||
|
|
||||||
#endif /* _HAPROXY_QUIC_TRACE_T_H */
|
#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_rx);
|
||||||
quic_tls_ku_reset(&qc->ku.nxt_tx);
|
quic_tls_ku_reset(&qc->ku.nxt_tx);
|
||||||
|
|
||||||
|
#ifdef HAVE_OPENSSL_QUIC
|
||||||
|
qc->prot_level = OSSL_RECORD_PROTECTION_LEVEL_NONE;
|
||||||
|
#endif
|
||||||
/* Encryption levels */
|
/* Encryption levels */
|
||||||
qc->iel = qc->eel = qc->hel = qc->ael = NULL;
|
qc->iel = qc->eel = qc->hel = qc->ael = NULL;
|
||||||
LIST_INIT(&qc->qel_list);
|
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/ssl_sock.h>
|
||||||
#include <haproxy/trace.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));
|
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
|
/* 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)
|
const struct quic_version *ver, int server)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
#ifdef USE_QUIC_OPENSSL_COMPAT
|
#if defined(USE_QUIC_OPENSSL_COMPAT) || defined(HAVE_OPENSSL_QUIC)
|
||||||
unsigned char *in = qc->enc_params;
|
unsigned char *in = qc->enc_params;
|
||||||
size_t insz = sizeof qc->enc_params;
|
size_t insz = sizeof qc->enc_params;
|
||||||
size_t *enclen = &qc->enc_params_len;
|
size_t *enclen = &qc->enc_params_len;
|
||||||
@ -327,27 +325,6 @@ write:
|
|||||||
return ret;
|
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
|
/* ->add_handshake_data QUIC TLS callback used by the QUIC TLS stack when it
|
||||||
* wants to provide the QUIC layer with CRYPTO data.
|
* wants to provide the QUIC layer with CRYPTO data.
|
||||||
* Returns 1 if succeeded, 0 if not.
|
* 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;
|
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)
|
static int ha_quic_flush_flight(SSL *ssl)
|
||||||
{
|
{
|
||||||
struct quic_conn *qc = SSL_get_ex_data(ssl, ssl_qc_app_data_index);
|
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,
|
.send_alert = ha_quic_send_alert,
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
#endif /* HAVE_OPENSSL_QUIC */
|
||||||
|
|
||||||
/* Initialize the TLS context of a listener with <bind_conf> as configuration.
|
/* Initialize the TLS context of a listener with <bind_conf> as configuration.
|
||||||
* Returns an error count.
|
* 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);
|
TRACE_ENTER(QUIC_EV_CONN_SSLDATA, qc);
|
||||||
|
|
||||||
|
#ifndef HAVE_OPENSSL_QUIC
|
||||||
if (SSL_provide_quic_data(ctx->ssl, level, data, len) != 1) {
|
if (SSL_provide_quic_data(ctx->ssl, level, data, len) != 1) {
|
||||||
TRACE_ERROR("SSL_provide_quic_data() error",
|
TRACE_ERROR("SSL_provide_quic_data() error",
|
||||||
QUIC_EV_CONN_SSLDATA, qc, NULL, NULL, ctx->ssl);
|
QUIC_EV_CONN_SSLDATA, qc, NULL, NULL, ctx->ssl);
|
||||||
goto leave;
|
goto leave;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
state = qc->state;
|
state = qc->state;
|
||||||
if (state < QUIC_HS_ST_COMPLETE) {
|
if (state < QUIC_HS_ST_COMPLETE) {
|
||||||
@ -609,7 +874,27 @@ static int qc_ssl_provide_quic_data(struct ncbuf *ncbuf,
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef HAVE_OPENSSL_QUIC
|
||||||
TRACE_PROTO("SSL handshake OK", QUIC_EV_CONN_IO_CB, qc, &state);
|
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 */
|
/* Check the alpn could be negotiated */
|
||||||
if (!qc->app_ops) {
|
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);
|
TRACE_ERROR("quic_tls_key_update() failed", QUIC_EV_CONN_IO_CB, qc);
|
||||||
goto leave;
|
goto leave;
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
#ifndef HAVE_OPENSSL_QUIC
|
||||||
|
else {
|
||||||
ssl_err = SSL_process_quic_post_handshake(ctx->ssl);
|
ssl_err = SSL_process_quic_post_handshake(ctx->ssl);
|
||||||
if (ssl_err != 1) {
|
if (ssl_err != 1) {
|
||||||
ssl_err = SSL_get_error(ctx->ssl, ssl_err);
|
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);
|
TRACE_STATE("SSL post handshake succeeded", QUIC_EV_CONN_IO_CB, qc, &state);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
out:
|
out:
|
||||||
ret = 1;
|
ret = 1;
|
||||||
@ -735,6 +1023,16 @@ int qc_ssl_provide_all_quic_data(struct quic_conn *qc, struct ssl_sock_ctx *ctx)
|
|||||||
return ret;
|
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
|
/* Try to allocate the <*ssl> SSL session object for <qc> QUIC connection
|
||||||
* with <ssl_ctx> as SSL context inherited settings. Also set the transport
|
* with <ssl_ctx> as SSL context inherited settings. Also set the transport
|
||||||
* parameters of this session.
|
* 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);
|
*ssl = SSL_new(ssl_ctx);
|
||||||
if (!*ssl) {
|
if (!*ssl) {
|
||||||
if (!retry--)
|
if (!retry--)
|
||||||
goto leave;
|
goto err;
|
||||||
|
|
||||||
pool_gc(NULL);
|
pool_gc(NULL);
|
||||||
goto retry;
|
goto retry;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!SSL_set_ex_data(*ssl, ssl_qc_app_data_index, qc) ||
|
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_free(*ssl);
|
||||||
*ssl = NULL;
|
*ssl = NULL;
|
||||||
if (!retry--)
|
if (!retry--)
|
||||||
goto leave;
|
goto err;
|
||||||
|
|
||||||
pool_gc(NULL);
|
pool_gc(NULL);
|
||||||
goto retry;
|
goto retry;
|
||||||
@ -775,6 +1073,9 @@ static int qc_ssl_sess_init(struct quic_conn *qc, SSL_CTX *ssl_ctx, SSL **ssl)
|
|||||||
leave:
|
leave:
|
||||||
TRACE_LEAVE(QUIC_EV_CONN_NEW, qc);
|
TRACE_LEAVE(QUIC_EV_CONN_NEW, qc);
|
||||||
return ret;
|
return ret;
|
||||||
|
err:
|
||||||
|
TRACE_DEVEL("leaving on error", QUIC_EV_CONN_NEW, qc);
|
||||||
|
goto leave;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAVE_SSL_0RTT_QUIC
|
#ifdef HAVE_SSL_0RTT_QUIC
|
||||||
@ -872,18 +1173,7 @@ int qc_alloc_ssl_sock_ctx(struct quic_conn *qc)
|
|||||||
return !ret;
|
return !ret;
|
||||||
|
|
||||||
err:
|
err:
|
||||||
|
TRACE_DEVEL("leaving on error", QUIC_EV_CONN_NEW, qc);
|
||||||
pool_free(pool_head_quic_ssl_sock_ctx, ctx);
|
pool_free(pool_head_quic_ssl_sock_ctx, ctx);
|
||||||
goto leave;
|
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/proto_tcp.h>
|
||||||
#include <haproxy/quic_conn.h>
|
#include <haproxy/quic_conn.h>
|
||||||
#include <haproxy/quic_openssl_compat.h>
|
#include <haproxy/quic_openssl_compat.h>
|
||||||
|
#include <haproxy/quic_ssl.h>
|
||||||
#include <haproxy/quic_tp.h>
|
#include <haproxy/quic_tp.h>
|
||||||
#include <haproxy/ssl_ckch.h>
|
#include <haproxy/ssl_ckch.h>
|
||||||
#include <haproxy/ssl_gencert.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_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_client_CA_list(ssl, SSL_dup_CA_list(SSL_CTX_get_client_CA_list(ctx)));
|
||||||
SSL_set_SSL_CTX(ssl, 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/errors.h>
|
||||||
#include <haproxy/openssl-compat.h>
|
#include <haproxy/openssl-compat.h>
|
||||||
|
#include <haproxy/quic_ssl.h>
|
||||||
#include <haproxy/ssl_ckch.h>
|
#include <haproxy/ssl_ckch.h>
|
||||||
#include <haproxy/ssl_sock.h>
|
#include <haproxy/ssl_sock.h>
|
||||||
#include <haproxy/xxhash.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);
|
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);
|
lru = lru64_lookup(key, ssl_ctx_lru_tree, bind_conf->ca_sign_ckch->cert, 0);
|
||||||
if (lru && lru->domain) {
|
if (lru && lru->domain) {
|
||||||
if (ssl)
|
if (ssl) {
|
||||||
SSL_set_SSL_CTX(ssl, (SSL_CTX *)lru->data);
|
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);
|
HA_RWLOCK_WRUNLOCK(SSL_GEN_CERTS_LOCK, &ssl_ctx_lru_rwlock);
|
||||||
return (SSL_CTX *)lru->data;
|
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);
|
lru64_commit(lru, ssl_ctx, cacert, 0, (void (*)(void *))SSL_CTX_free);
|
||||||
}
|
}
|
||||||
SSL_set_SSL_CTX(ssl, ssl_ctx);
|
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);
|
HA_RWLOCK_WRUNLOCK(SSL_GEN_CERTS_LOCK, &ssl_ctx_lru_rwlock);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
ssl_ctx = ssl_sock_do_create_cert(servername, bind_conf, ssl);
|
ssl_ctx = ssl_sock_do_create_cert(servername, bind_conf, ssl);
|
||||||
SSL_set_SSL_CTX(ssl, ssl_ctx);
|
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 */
|
/* No LRU cache, this CTX will be released as soon as the session dies */
|
||||||
SSL_CTX_free(ssl_ctx);
|
SSL_CTX_free(ssl_ctx);
|
||||||
return 1;
|
return 1;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user