diff --git a/include/haproxy/quic_ssl.h b/include/haproxy/quic_ssl.h index 3f0c2010c..f22b7c03e 100644 --- a/include/haproxy/quic_ssl.h +++ b/include/haproxy/quic_ssl.h @@ -81,7 +81,12 @@ static inline const char *quic_ssl_early_data_status_str(const SSL *ssl) } #endif } -#else +#else /* !HAVE_SSL_0RTT_QUIC */ +static inline int qc_ssl_eary_data_accepted(const SSL *ssl) +{ + return 0; +} + static inline const char *quic_ssl_early_data_status_str(const SSL *ssl) { return "NOT_SUPPORTED"; diff --git a/include/haproxy/quic_tp.h b/include/haproxy/quic_tp.h index 299de9872..ce7ec59ef 100644 --- a/include/haproxy/quic_tp.h +++ b/include/haproxy/quic_tp.h @@ -18,7 +18,7 @@ int quic_transport_params_encode(unsigned char *buf, int quic_transport_params_store(struct quic_conn *conn, int server, const unsigned char *buf, - const unsigned char *end); + const unsigned char *end, int edata_accepted); void qc_early_transport_params_cpy(struct quic_conn *qc, struct quic_early_transport_params *e, struct quic_transport_params *p); diff --git a/src/quic_ssl.c b/src/quic_ssl.c index bdc5311f6..4d7798b86 100644 --- a/src/quic_ssl.c +++ b/src/quic_ssl.c @@ -309,9 +309,12 @@ write: if (qc_is_back(qc)) { const unsigned char *tp; size_t tplen; + int edata_accepted = qc_ssl_eary_data_accepted(ssl); + TRACE_PROTO("storing peer transport parameters", + QUIC_EV_CONN_IO_CB, qc, NULL, NULL, ssl); SSL_get_peer_quic_transport_params(ssl, &tp, &tplen); - if (!tplen || !quic_transport_params_store(qc, 1,tp, tp + tplen)) { + if (!tplen || !quic_transport_params_store(qc, 1,tp, tp + tplen, edata_accepted)) { TRACE_ERROR("Could not parse remote transport paratemers", QUIC_EV_CONN_RWSEC, qc); goto leave; @@ -602,8 +605,12 @@ static int ha_quic_ossl_got_transport_params(SSL *ssl, const unsigned char *para QUIC_EV_TRANSP_PARAMS, qc); ret = 1; } - else if (!quic_transport_params_store(qc, qc_is_back(qc), params, params + params_len)) { - goto err; + else { + TRACE_PROTO("storing peer transport parameters", + QUIC_EV_CONN_IO_CB, qc, NULL, NULL, ssl); + if (!quic_transport_params_store(qc, qc_is_back(qc), params, params + params_len, + qc_ssl_eary_data_accepted(ssl))) + goto err; } ret = 1; diff --git a/src/quic_tp.c b/src/quic_tp.c index 2e35f2e1f..2c50b1281 100644 --- a/src/quic_tp.c +++ b/src/quic_tp.c @@ -12,6 +12,10 @@ #define QUIC_MAX_UDP_PAYLOAD_SIZE 2048 +static int qc_early_tranport_params_validate(struct quic_conn *qc, + struct quic_transport_params *p, + struct quic_early_transport_params *e); + /* This is the values of some QUIC transport parameters when absent. * Should be used to initialize any transport parameters (local or remote) * before updating them with customized values. @@ -734,17 +738,25 @@ quic_transport_params_decode(struct quic_transport_params *p, int server, * * Returns 1 on success, or 0 if parsing is interrupted on a truncated field. * Note that if invalid values are used, success is returned by this function - * but the connection is scheduled for CONNECTION_CLOSE emission. + * but the connection is scheduled for CONNECTION_CLOSE emission. This is also + * the case when the early transport parameters could not be validated. */ int quic_transport_params_store(struct quic_conn *qc, int server, const unsigned char *buf, - const unsigned char *end) + const unsigned char *end, int edata_accepted) { enum quic_tp_dec_err err; struct quic_transport_params *tx_params = &qc->tx.params; struct quic_transport_params *rx_params = &qc->rx.params; /* Initial source connection ID */ struct tp_cid *iscid; + struct quic_early_transport_params etps; + + BUG_ON(edata_accepted && !qc_is_back(qc)); + if (edata_accepted) { + /* Local copy of early transport parameters */ + qc_early_transport_params_cpy(qc, &etps, tx_params); + } /* initialize peer TPs to RFC default value */ quic_dflt_transport_params_cpy(tx_params); @@ -760,6 +772,12 @@ int quic_transport_params_store(struct quic_conn *qc, int server, return 0; } + if (edata_accepted && !qc_early_tranport_params_validate(qc, tx_params, &etps)) { + TRACE_ERROR("could not validate early transport parameters", QUIC_EV_TRANSP_PARAMS, qc); + quic_set_connection_close(qc, quic_err_transport(QC_ERR_PROTOCOL_VIOLATION)); + return 1; + } + if (server && qc->retry_token) { if (!tx_params->retry_source_connection_id.len || (qc->odcid.len != tx_params->retry_source_connection_id.len || @@ -896,3 +914,34 @@ void qc_early_transport_params_reuse(struct quic_conn *qc, TRACE_PROTO("\nTX(remote) reuse early transp. params.", QUIC_EV_EARLY_TRANSP_PARAMS, qc, e); TRACE_PROTO("\nTX(remote) transp. params.", QUIC_EV_TRANSP_PARAMS, qc, p); } + +static int qc_early_tranport_params_validate(struct quic_conn *qc, + struct quic_transport_params *p, + struct quic_early_transport_params *e) +{ + /* RFC 9000 + * 7.4.1. Values of Transport Parameters for 0-RTT + * If 0-RTT data is accepted by the server, the server MUST NOT reduce any + * limits or alter any values that might be violated by the client with its + * 0-RTT data. In particular, a server that accepts 0-RTT data MUST NOT set + * values for the following parameters (Section 18.2) that are smaller than + * the remembered values of the parameters. + * - active_connection_id_limit + * - initial_max_data + * - initial_max_stream_data_bidi_local + * - initial_max_stream_data_bidi_remote + * - initial_max_stream_data_uni + * - initial_max_streams_bidi + * - initial_max_streams_uni + */ + if (e->active_connection_id_limit > p->active_connection_id_limit || + e->initial_max_data > p->initial_max_data || + e->initial_max_stream_data_bidi_local > p->initial_max_stream_data_bidi_local || + e->initial_max_stream_data_bidi_remote > p->initial_max_stream_data_bidi_remote || + e->initial_max_stream_data_uni > p->initial_max_stream_data_uni || + e->initial_max_streams_bidi > p->initial_max_streams_bidi || + e->initial_max_streams_uni > p->initial_max_streams_uni) + return 0; + + return 1; +} diff --git a/src/ssl_clienthello.c b/src/ssl_clienthello.c index 1129f0c4e..ffc06b94b 100644 --- a/src/ssl_clienthello.c +++ b/src/ssl_clienthello.c @@ -206,7 +206,7 @@ int ssl_sock_switchctx_cbk(SSL *ssl, int *al, void *arg) } if (!quic_transport_params_store(qc, 0, extension_data, - extension_data + extension_len)) + extension_data + extension_len, 0)) goto abort; qc->flags |= QUIC_FL_CONN_TX_TP_RECEIVED; @@ -565,7 +565,7 @@ int ssl_sock_switchctx_cbk(SSL *ssl, int *al, void *priv) } if (!quic_transport_params_store(qc, 0, extension_data, - extension_data + extension_len)) + extension_data + extension_len, 0)) return SSL_TLSEXT_ERR_NOACK; qc->flags |= QUIC_FL_CONN_TX_TP_RECEIVED;