Skip to content

Commit 5075a21

Browse files
committed
MINOR: quic: implement all remaining callbacks for OpenSSL 3.5 QUIC API
The quic_conn struct is modified for two reasons. The first one is to store the encoded version of the local tranport parameter as this is done for USE_QUIC_OPENSSL_COMPAT. Indeed, the local transport parameter "should remain valid until after the parameters have been sent" as mentionned by SSL_set_quic_tls_cbs(3) manual. In our case, the buffer is a static buffer attached to the quic_conn object. qc_ssl_set_quic_transport_params() function whose role is to call SSL_set_tls_quic_transport_params() (aliased by SSL_set_quic_transport_params() to set these local tranport parameter into the TLS stack from the buffer attached to the quic_conn struct. The second quic_conn struct modification is the addition of the new ->prot_level (SSL protection level) member is added to the quic_conn strut to store "the most recent write encryption level set via the OSSL_FUNC_SSL_QUIC_TLS_yield_secret_fn callback (if it has been called)" as mentionned by SSL_set_quic_tls_cbs(3) manual. This patches finally implements the five remaining callacks to make the haproxy QUIC implementation work. OSSL_FUNC_SSL_QUIC_TLS_crypto_send_fn() (ha_quic_ossl_crypto_send) is easy to implement. It calls ha_quic_add_handshake_data() after having converted qc->prot_level TLS protection level value to the correct ssl_encryption_level_t (boringSSL API/quictls) value. OSSL_FUNC_SSL_QUIC_TLS_crypto_recv_rcd_fn() (ha_quic_ossl_crypto_recv_rcd()) provide the non-contiguous addresses to the TLS stack, without releasing them. OSSL_FUNC_SSL_QUIC_TLS_crypto_release_rcd_fn() (ha_quic_ossl_crypto_release_rcd()) release these non-contiguous buffer relying on the fact that the list of encryption level (qc->qel_list) is correctly ordered by SSL protection level secret establishements order (by the TLS stack). OSSL_FUNC_SSL_QUIC_TLS_yield_secret_fn() (ha_quic_ossl_got_transport_params()) is a simple wrapping function over ha_quic_set_encryption_secrets() which is used by boringSSL/quictls API. OSSL_FUNC_SSL_QUIC_TLS_got_transport_params_fn() (ha_quic_ossl_got_transport_params()) role is to store the peer received transport parameters. It simply calls quic_transport_params_store() and set them into the TLS stack calling qc_ssl_set_quic_transport_params(). Also add some comments for all the OpenSSL 3.5 QUIC API callbacks. This patch have no impact on the other use of QUIC API provided by the others TLS stacks.
1 parent f0c833f commit 5075a21

File tree

4 files changed

+194
-11
lines changed

4 files changed

+194
-11
lines changed

include/haproxy/quic_conn-t.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,7 @@ struct quic_conn {
334334
int tps_tls_ext;
335335
int state;
336336
enum qc_mux_state mux_state; /* status of the connection/mux layer */
337-
#ifdef USE_QUIC_OPENSSL_COMPAT
337+
#if defined(USE_QUIC_OPENSSL_COMPAT) || defined(HAVE_OPENSSL_QUIC)
338338
unsigned char enc_params[QUIC_TP_MAX_ENCLEN]; /* encoded QUIC transport parameters */
339339
size_t enc_params_len;
340340
#endif
@@ -345,6 +345,9 @@ struct quic_conn {
345345
*/
346346
uint64_t hash64;
347347

348+
#ifdef HAVE_OPENSSL_QUIC
349+
uint32_t prot_level;
350+
#endif
348351
/* Initial encryption level */
349352
struct quic_enc_level *iel;
350353
/* 0-RTT encryption level */

include/haproxy/quic_tls.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,29 @@ static inline struct quic_enc_level **ssl_to_qel_addr(struct quic_conn *qc,
291291
}
292292
}
293293

294+
#ifdef HAVE_OPENSSL_QUIC
295+
/* Simple helper function which translate an OpenSSL SSL protection level
296+
* to a quictls SSL encryption. This way the code which use the OpenSSL QUIC API
297+
* may use the code which uses the quictls API.
298+
*/
299+
static inline enum ssl_encryption_level_t ssl_prot_level_to_enc_level(struct quic_conn *qc,
300+
uint32_t prot_level)
301+
{
302+
switch (prot_level) {
303+
case OSSL_RECORD_PROTECTION_LEVEL_NONE:
304+
return ssl_encryption_initial;
305+
case OSSL_RECORD_PROTECTION_LEVEL_EARLY:
306+
return ssl_encryption_early_data;
307+
case OSSL_RECORD_PROTECTION_LEVEL_HANDSHAKE:
308+
return ssl_encryption_handshake;
309+
case OSSL_RECORD_PROTECTION_LEVEL_APPLICATION:
310+
return ssl_encryption_application;
311+
default:
312+
return -1;
313+
}
314+
}
315+
#endif
316+
294317
/* Return the address of the QUIC TLS encryption level associated to <level> internal
295318
* encryption level and attached to <qc> QUIC connection if succeeded, or
296319
* NULL if failed.

src/quic_conn.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1109,6 +1109,9 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
11091109
quic_tls_ku_reset(&qc->ku.nxt_rx);
11101110
quic_tls_ku_reset(&qc->ku.nxt_tx);
11111111

1112+
#ifdef HAVE_OPENSSL_QUIC
1113+
qc->prot_level = OSSL_RECORD_PROTECTION_LEVEL_NONE;
1114+
#endif
11121115
/* Encryption levels */
11131116
qc->iel = qc->eel = qc->hel = qc->ael = NULL;
11141117
LIST_INIT(&qc->qel_list);

src/quic_ssl.c

Lines changed: 164 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ static int qc_ssl_set_quic_transport_params(struct quic_conn *qc,
2121
const struct quic_version *ver, int server)
2222
{
2323
int ret = 0;
24-
#ifdef USE_QUIC_OPENSSL_COMPAT
24+
#if defined(USE_QUIC_OPENSSL_COMPAT) || defined(HAVE_OPENSSL_QUIC)
2525
unsigned char *in = qc->enc_params;
2626
size_t insz = sizeof qc->enc_params;
2727
size_t *enclen = &qc->enc_params_len;
@@ -371,68 +371,222 @@ static int ha_quic_add_handshake_data(SSL *ssl, enum ssl_encryption_level_t leve
371371
#ifdef HAVE_OPENSSL_QUIC
372372
/************************** OpenSSL QUIC TLS API (>= 3.5.0) *******************/
373373

374+
/* Callback called by OpenSSL when it needs to send CRYPTO data to the peer.
375+
* This is done from <buf> buffer with <buf_len> as number of bytes to be sent.
376+
* This callback must set <*consumed> to the number of bytes which could be
377+
* consumed (buffered in our case) before being sent to the peer. This is always
378+
* <buf_len> when this callback succeeds, or 0 when it fails.
379+
* Return 1 if succeeded, 0 if not.
380+
*/
374381
static int ha_quic_ossl_crypto_send(SSL *ssl,
375382
const unsigned char *buf, size_t buf_len,
376383
size_t *consumed, void *arg)
377384
{
378385
int ret = 0;
379386
struct quic_conn *qc = SSL_get_ex_data(ssl, ssl_qc_app_data_index);
387+
enum ssl_encryption_level_t level = ssl_prot_level_to_enc_level(qc, qc->prot_level);
380388

381389
TRACE_ENTER(QUIC_EV_CONN_ADDDATA, qc);
382-
TRACE_LEAVE(QUIC_EV_CONN_ADDDATA, qc);
383390

391+
if (!ha_quic_add_handshake_data(ssl, level, buf, buf_len))
392+
goto err;
393+
394+
*consumed = buf_len;
395+
TRACE_DEVEL("CRYPTO data buffered", QUIC_EV_CONN_ADDDATA, qc, &level, &buf_len);
396+
397+
ret = 1;
398+
leave:
399+
TRACE_LEAVE(QUIC_EV_CONN_ADDDATA, qc);
384400
return ret;
401+
err:
402+
*consumed = 0;
403+
TRACE_DEVEL("leaving on error", QUIC_EV_CONN_ADDDATA, qc);
404+
goto leave;
385405
}
386406

407+
/* Callback to provide CRYPTO data from the peer to the TLS stack. It must set
408+
* <buf> to the address of the buffer which contains the CRYPTO data.
409+
* <*byte_read> value must be the number of bytes of CRYPTO data received.
410+
* Never fail, always return 1.
411+
*/
387412
static int ha_quic_ossl_crypto_recv_rcd(SSL *ssl,
388-
const unsigned char **buf, size_t *bytes_read,
413+
const unsigned char **buf,
414+
size_t *bytes_read,
389415
void *arg)
390416
{
391-
int ret = 0;
392417
struct quic_conn *qc = SSL_get_ex_data(ssl, ssl_qc_app_data_index);
418+
struct quic_enc_level *qel;
419+
struct ncbuf *ncbuf = NULL;
420+
struct quic_cstream *cstream = NULL;
421+
ncb_sz_t data = 0;
393422

394423
TRACE_ENTER(QUIC_EV_CONN_SSLDATA, qc);
395-
TRACE_LEAVE(QUIC_EV_CONN_SSLDATA, qc);
396424

397-
return ret;
425+
list_for_each_entry(qel, &qc->qel_list, list) {
426+
cstream = qel->cstream;
427+
if (!cstream)
428+
continue;
429+
430+
ncbuf = &cstream->rx.ncbuf;
431+
if (ncb_is_null(ncbuf))
432+
continue;
433+
434+
data = ncb_data(ncbuf, 0);
435+
if (data)
436+
break;
437+
}
438+
439+
if (data) {
440+
const unsigned char *cdata;
441+
442+
BUG_ON(ncb_is_null(ncbuf) || !cstream);
443+
/* <ncbuf> must not be released at this time. */
444+
cdata = (const unsigned char *)ncb_head(ncbuf);
445+
cstream->rx.offset += data;
446+
TRACE_DEVEL("buffered crypto data were provided to TLS stack",
447+
QUIC_EV_CONN_PHPKTS, qc, qel);
448+
*buf = cdata;
449+
*bytes_read = data;
450+
}
451+
else {
452+
*buf = NULL;
453+
*bytes_read = 0;
454+
}
455+
456+
TRACE_LEAVE(QUIC_EV_CONN_SSLDATA, qc);
457+
return 1;
398458
}
399459

400-
static int ha_quic_ossl_crypto_release_rcd(SSL *ssl, size_t bytes_read, void *arg)
460+
/* Callback to release the CRYPT data buffer which have been received
461+
* by ha_quic_ossl_crypto_recv_rcd().
462+
* Return 0 if failed, this means no buffer could be released, or 1 if
463+
* succeeded.
464+
*/
465+
static int ha_quic_ossl_crypto_release_rcd(SSL *ssl,
466+
size_t bytes_read, void *arg)
401467
{
402468
int ret = 0;
403469
struct quic_conn *qc = SSL_get_ex_data(ssl, ssl_qc_app_data_index);
470+
struct quic_enc_level *qel;
404471

405472
TRACE_ENTER(QUIC_EV_CONN_RELEASE_RCD, qc);
406-
TRACE_LEAVE(QUIC_EV_CONN_RELEASE_RCD, qc);
407473

474+
list_for_each_entry(qel, &qc->qel_list, list) {
475+
struct quic_cstream *cstream = qel->cstream;
476+
struct ncbuf *ncbuf;
477+
ncb_sz_t data;
478+
479+
if (!cstream)
480+
continue;
481+
482+
ncbuf = &cstream->rx.ncbuf;
483+
if (ncb_is_null(ncbuf))
484+
continue;
485+
486+
data = ncb_data(ncbuf, 0);
487+
if (!data)
488+
continue;
489+
490+
data = data > bytes_read ? bytes_read : data;
491+
ncb_advance(ncbuf, data);
492+
bytes_read -= data;
493+
if (ncb_is_empty(ncbuf)) {
494+
TRACE_DEVEL("freeing crypto buf", QUIC_EV_CONN_PHPKTS, qc, qel);
495+
quic_free_ncbuf(ncbuf);
496+
}
497+
498+
ret = 1;
499+
if (bytes_read == 0)
500+
break;
501+
}
502+
503+
if (!ret)
504+
goto err;
505+
leave:
506+
TRACE_LEAVE(QUIC_EV_CONN_RELEASE_RCD, qc);
408507
return ret;
508+
err:
509+
TRACE_DEVEL("leaving on error", QUIC_EV_CONN_RELEASE_RCD, qc);
510+
goto leave;
409511
}
410512

513+
/* Callback called by OpenSSL when <secret> has been established at
514+
* <prot_level> SSL protection level. <direction> value is 0 for a read secret,
515+
* 1 for a write secret.
516+
* Return 1 if succeeded, 0 if not.
517+
*/
411518
static int ha_quic_ossl_yield_secret(SSL *ssl, uint32_t prot_level, int direction,
412519
const unsigned char *secret, size_t secret_len,
413520
void *arg)
414521
{
415522
int ret = 0;
416523
struct quic_conn *qc = SSL_get_ex_data(ssl, ssl_qc_app_data_index);
524+
enum ssl_encryption_level_t level = ssl_prot_level_to_enc_level(qc, prot_level);
417525

418526
TRACE_ENTER(QUIC_EV_CONN_RWSEC, qc);
419-
TRACE_LEAVE(QUIC_EV_CONN_RWSEC, qc);
420527

528+
BUG_ON(level == -1);
529+
530+
if (!direction) {
531+
/* read secret */
532+
if (!ha_quic_set_encryption_secrets(ssl, level, secret, NULL, secret_len))
533+
goto err;
534+
}
535+
else {
536+
/* write secret */
537+
if (!ha_quic_set_encryption_secrets(ssl, level, NULL, secret, secret_len))
538+
goto err;
539+
540+
qc->prot_level = prot_level;
541+
}
542+
543+
ret = 1;
544+
leave:
545+
TRACE_LEAVE(QUIC_EV_CONN_RWSEC, qc);
421546
return ret;
547+
err:
548+
TRACE_DEVEL("leaving on error", QUIC_EV_CONN_RWSEC, qc);
549+
goto leave;
422550
}
423551

552+
/* Callback called by OpenSSL when the peer transport parameters have been
553+
* received.
554+
* Return 1 if succeeded, 0 if not.
555+
*/
424556
static int ha_quic_ossl_got_transport_params(SSL *ssl, const unsigned char *params,
425557
size_t params_len, void *arg)
426558
{
427559
int ret = 0;
428560
struct quic_conn *qc = SSL_get_ex_data(ssl, ssl_qc_app_data_index);
561+
const struct quic_version *ver =
562+
qc->negotiated_version ? qc->negotiated_version : qc->original_version;
429563

430564
TRACE_ENTER(QUIC_EV_TRANSP_PARAMS, qc);
431-
TRACE_LEAVE(QUIC_EV_TRANSP_PARAMS, qc);
432565

566+
if (qc->flags & QUIC_FL_CONN_TX_TP_RECEIVED) {
567+
TRACE_PROTO("peer transport parameters already received",
568+
QUIC_EV_TRANSP_PARAMS, qc);
569+
ret = 1;
570+
}
571+
else {
572+
if (!quic_transport_params_store(qc, 0, params, params + params_len) ||
573+
!qc_ssl_set_quic_transport_params(qc, ver, 1))
574+
goto err;
575+
}
576+
577+
ret = 1;
578+
leave:
579+
TRACE_LEAVE(QUIC_EV_TRANSP_PARAMS, qc);
433580
return ret;
581+
err:
582+
TRACE_DEVEL("leaving on error", QUIC_EV_CONN_RWSEC, qc);
583+
goto leave;
434584
}
435585

586+
/* Callback called by OpenSSL when it needs to send a TLS to the peer with
587+
* <alert_code> as value.
588+
* Always succeeds.
589+
*/
436590
static int ha_quic_ossl_alert(SSL *ssl, unsigned char alert_code, void *arg)
437591
{
438592
int ret = 1, alert = alert_code;

0 commit comments

Comments
 (0)